Native Transport Netty Class Package Prefix
Motivation: transport-native-epoll finds java classes from JNI using fully qualified class names. If a shaded version of Netty is used then these lookups will fail. Modifications: - Allow a prefix to be appended to Netty class names in JNI code. Result: JNI code can be used with shaded version of Netty.
This commit is contained in:
parent
bf24ffd335
commit
dea337b4cf
@ -38,6 +38,7 @@
|
|||||||
#include "netty_unix_filedescriptor.h"
|
#include "netty_unix_filedescriptor.h"
|
||||||
#include "netty_unix_socket.h"
|
#include "netty_unix_socket.h"
|
||||||
#include "netty_unix_errors.h"
|
#include "netty_unix_errors.h"
|
||||||
|
#include "netty_unix_util.h"
|
||||||
|
|
||||||
// TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile.
|
// TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile.
|
||||||
#ifndef TCP_NOTSENT_LOWAT
|
#ifndef TCP_NOTSENT_LOWAT
|
||||||
@ -84,6 +85,9 @@ jfieldID packetScopeIdFieldId = NULL;
|
|||||||
jfieldID packetPortFieldId = NULL;
|
jfieldID packetPortFieldId = NULL;
|
||||||
jfieldID packetMemoryAddressFieldId = NULL;
|
jfieldID packetMemoryAddressFieldId = NULL;
|
||||||
jfieldID packetCountFieldId = NULL;
|
jfieldID packetCountFieldId = NULL;
|
||||||
|
static jstring nettyPackagePrefixJString = NULL;
|
||||||
|
static const char* nettyPackagePrefix = NULL;
|
||||||
|
static char* nettyClassName = NULL;
|
||||||
|
|
||||||
// util methods
|
// util methods
|
||||||
static int getSysctlValue(const char * property, int* returnValue) {
|
static int getSysctlValue(const char * property, int* returnValue) {
|
||||||
@ -116,17 +120,35 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
} else {
|
} else {
|
||||||
if (netty_unix_errors_JNI_OnLoad(env) == JNI_ERR) {
|
// Load the prefix to use when looking for Netty classes
|
||||||
|
jclass systemCls = (*env)->FindClass(env, "java/lang/System");
|
||||||
|
if (systemCls == NULL) {
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
if (netty_unix_filedescriptor_JNI_OnLoad(env) == JNI_ERR) {
|
jmethodID getPropertyMethod = (*env)->GetStaticMethodID(env, systemCls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||||
|
if (getPropertyMethod == NULL) {
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
if (netty_unix_socket_JNI_OnLoad(env) == JNI_ERR) {
|
jstring propertyName = (*env)->NewStringUTF(env, "io.netty.native.epoll.nettyPackagePrefix");
|
||||||
|
nettyPackagePrefixJString = (*env)->CallStaticObjectMethod(env, systemCls, getPropertyMethod, propertyName);
|
||||||
|
if (nettyPackagePrefixJString != NULL) {
|
||||||
|
nettyPackagePrefix = (*env)->GetStringUTFChars(env, nettyPackagePrefixJString, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netty_unix_errors_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
if (netty_unix_filedescriptor_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
if (netty_unix_socket_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass fileRegionCls = (*env)->FindClass(env, "io/netty/channel/DefaultFileRegion");
|
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/DefaultFileRegion");
|
||||||
|
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
if (fileRegionCls == NULL) {
|
if (fileRegionCls == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
@ -164,7 +186,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass nativeDatagramPacketCls = (*env)->FindClass(env, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
|
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
|
||||||
|
jclass nativeDatagramPacketCls = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
if (nativeDatagramPacketCls == NULL) {
|
if (nativeDatagramPacketCls == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
@ -197,6 +222,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nettyPackagePrefixJString != NULL) {
|
||||||
|
(*env)->ReleaseStringUTFChars(env, nettyPackagePrefixJString, nettyPackagePrefix);
|
||||||
|
nettyPackagePrefix = NULL;
|
||||||
|
nettyPackagePrefixJString = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,6 +239,15 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// delete global references so the GC can collect them
|
// delete global references so the GC can collect them
|
||||||
|
if (nettyPackagePrefixJString != NULL) {
|
||||||
|
(*env)->ReleaseStringUTFChars(env, nettyPackagePrefixJString, nettyPackagePrefix);
|
||||||
|
nettyPackagePrefix = NULL;
|
||||||
|
nettyPackagePrefixJString = NULL;
|
||||||
|
}
|
||||||
|
if (nettyClassName != NULL) {
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
|
}
|
||||||
netty_unix_errors_JNI_OnUnLoad(env);
|
netty_unix_errors_JNI_OnUnLoad(env);
|
||||||
netty_unix_filedescriptor_JNI_OnUnLoad(env);
|
netty_unix_filedescriptor_JNI_OnUnLoad(env);
|
||||||
netty_unix_socket_JNI_OnUnLoad(env);
|
netty_unix_socket_JNI_OnUnLoad(env);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "netty_unix_errors.h"
|
#include "netty_unix_errors.h"
|
||||||
|
#include "netty_unix_util.h"
|
||||||
#include "io_netty_channel_unix_Errors.h"
|
#include "io_netty_channel_unix_Errors.h"
|
||||||
|
|
||||||
static jclass runtimeExceptionClass = NULL;
|
static jclass runtimeExceptionClass = NULL;
|
||||||
@ -24,22 +25,14 @@ static jclass channelExceptionClass = NULL;
|
|||||||
static jclass ioExceptionClass = NULL;
|
static jclass ioExceptionClass = NULL;
|
||||||
static jclass closedChannelExceptionClass = NULL;
|
static jclass closedChannelExceptionClass = NULL;
|
||||||
static jmethodID closedChannelExceptionMethodId = NULL;
|
static jmethodID closedChannelExceptionMethodId = NULL;
|
||||||
|
static char* nettyClassName = NULL;
|
||||||
|
|
||||||
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
|
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
|
||||||
static char* exceptionMessage(char* msg, int error) {
|
static char* exceptionMessage(char* msg, int error) {
|
||||||
if (error < 0) {
|
// strerror is returning a constant, so no need to free anything coming from strerror
|
||||||
// some functions return negative values
|
// error may be negative because some functions return negative values. we should make sure it is always
|
||||||
// and it's hard to keep track of when to send -error and when not
|
// positive when passing to standard library functions.
|
||||||
// this will just take care when things are forgotten
|
return netty_unix_util_prepend(msg, strerror(error < 0 ? -error : error));
|
||||||
// what would generate a proper error
|
|
||||||
error = error * -1;
|
|
||||||
}
|
|
||||||
//strerror is returning a constant, so no need to free anything coming from strerror
|
|
||||||
char* err = strerror(error);
|
|
||||||
char* result = malloc(strlen(msg) + strlen(err) + 1);
|
|
||||||
strcpy(result, msg);
|
|
||||||
strcat(result, err);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exported C methods
|
// Exported C methods
|
||||||
@ -79,7 +72,7 @@ void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
|
|||||||
(*env)->ThrowNew(env, exceptionClass, "");
|
(*env)->ThrowNew(env, exceptionClass, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
|
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
|
||||||
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
|
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
|
||||||
if (localRuntimeExceptionClass == NULL) {
|
if (localRuntimeExceptionClass == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
@ -92,7 +85,10 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException");
|
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/ChannelException");
|
||||||
|
jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
if (localChannelExceptionClass == NULL) {
|
if (localChannelExceptionClass == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
@ -139,6 +135,10 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
|
|||||||
|
|
||||||
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
|
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
|
||||||
// delete global references so the GC can collect them
|
// delete global references so the GC can collect them
|
||||||
|
if (nettyClassName != NULL) {
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
|
}
|
||||||
if (runtimeExceptionClass != NULL) {
|
if (runtimeExceptionClass != NULL) {
|
||||||
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
|
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
|
||||||
runtimeExceptionClass = NULL;
|
runtimeExceptionClass = NULL;
|
||||||
|
@ -74,7 +74,7 @@ static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, ji
|
|||||||
return (jint) res;
|
return (jint) res;
|
||||||
}
|
}
|
||||||
|
|
||||||
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env) {
|
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
|
||||||
void* mem = malloc(1);
|
void* mem = malloc(1);
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
netty_unix_errors_throwOutOfMemoryError(env);
|
netty_unix_errors_throwOutOfMemoryError(env);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "netty_unix_errors.h"
|
#include "netty_unix_errors.h"
|
||||||
#include "netty_unix_socket.h"
|
#include "netty_unix_socket.h"
|
||||||
|
#include "netty_unix_util.h"
|
||||||
#include "io_netty_channel_unix_Socket.h"
|
#include "io_netty_channel_unix_Socket.h"
|
||||||
|
|
||||||
static jclass datagramSocketAddressClass = NULL;
|
static jclass datagramSocketAddressClass = NULL;
|
||||||
@ -35,6 +36,7 @@ static jclass inetSocketAddressClass = NULL;
|
|||||||
static jclass netUtilClass = NULL;
|
static jclass netUtilClass = NULL;
|
||||||
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
||||||
static int socketType;
|
static int socketType;
|
||||||
|
static char* nettyClassName = NULL;
|
||||||
static const char* ip4prefix = "::ffff:";
|
static const char* ip4prefix = "::ffff:";
|
||||||
|
|
||||||
// Optional external methods
|
// Optional external methods
|
||||||
@ -301,8 +303,11 @@ int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, co
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
|
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
|
||||||
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/unix/DatagramSocketAddress");
|
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/unix/DatagramSocketAddress");
|
||||||
|
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
if (localDatagramSocketAddressClass == NULL) {
|
if (localDatagramSocketAddressClass == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
@ -334,7 +339,10 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
|
|||||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
|
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" );
|
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/util/NetUtil");
|
||||||
|
jclass localNetUtilClass = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
if (localNetUtilClass == NULL) {
|
if (localNetUtilClass == NULL) {
|
||||||
// pending exception...
|
// pending exception...
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
@ -376,6 +384,10 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
||||||
|
if (nettyClassName != NULL) {
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
|
}
|
||||||
if (datagramSocketAddressClass != NULL) {
|
if (datagramSocketAddressClass != NULL) {
|
||||||
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
|
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
|
||||||
datagramSocketAddressClass = NULL;
|
datagramSocketAddressClass = NULL;
|
||||||
|
@ -27,7 +27,7 @@ void netty_unix_errors_throwClosedChannelException(JNIEnv* env);
|
|||||||
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
|
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
|
||||||
|
|
||||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||||
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env);
|
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
|
||||||
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
|
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
|
||||||
|
|
||||||
#endif /* NETTY_UNIX_ERRORS_H_ */
|
#endif /* NETTY_UNIX_ERRORS_H_ */
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||||
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env);
|
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
|
||||||
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
|
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
|
||||||
|
|
||||||
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */
|
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */
|
||||||
|
@ -25,7 +25,7 @@ int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, vo
|
|||||||
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
|
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
|
||||||
|
|
||||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||||
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env);
|
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
|
||||||
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
|
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
|
||||||
|
|
||||||
#endif /* NETTY_UNIX_SOCKET_H_ */
|
#endif /* NETTY_UNIX_SOCKET_H_ */
|
||||||
|
30
transport-native-epoll/src/main/c/netty_unix_util.c
Normal file
30
transport-native-epoll/src/main/c/netty_unix_util.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "netty_unix_util.h"
|
||||||
|
|
||||||
|
char* netty_unix_util_prepend(const char* prefix, const char* str) {
|
||||||
|
if (prefix == NULL) {
|
||||||
|
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1));
|
||||||
|
strcpy(result, str);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1));
|
||||||
|
strcpy(result, prefix);
|
||||||
|
strcat(result, str);
|
||||||
|
return result;
|
||||||
|
}
|
27
transport-native-epoll/src/main/c/netty_unix_util.h
Normal file
27
transport-native-epoll/src/main/c/netty_unix_util.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETTY_UNIX_UTIL_H_
|
||||||
|
#define NETTY_UNIX_UTIL_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>.
|
||||||
|
*
|
||||||
|
* Caller must free the return value!
|
||||||
|
*/
|
||||||
|
char* netty_unix_util_prepend(const char* prefix, const char* str);
|
||||||
|
|
||||||
|
#endif /* NETTY_UNIX_UTIL_H_ */
|
Loading…
Reference in New Issue
Block a user