📄 octstr.c.debug
字号:
/* * octstr.c - implementation of Octet strings * * See octstr.h for explanations of what public functions should do. * * Lars Wirzenius */#include <ctype.h>#include <limits.h>#include <errno.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include "gwlib.h"/*********************************************************************** * Definitions of data structures. These are not visible to the external * world -- they may be accessed only via the functions declared in * octstr.h. This ensures they really are abstract. *//* * The octet string. * * `data' is a pointer to dynamically allocated memory are where the * octets in the string. It may be bigger than the actual length of the * string. * * `len' is the length of the string. * * `size' is the size of the memory area `data' points at. * * When `size' is greater than zero, it is at least `len+1', and the * character at `len' is '\0'. This is so that octstr_get_cstr will * always work. * * `immutable' defines whether the octet string is immutable or not. */struct Octstr{ unsigned char *data; long len; long size; int immutable;};/********************************************************************** * Hash table of immutable octet strings. */#define MAX_IMMUTABLES 1024static Octstr *immutables[MAX_IMMUTABLES];static Mutex immutables_mutex;static int immutables_init = 0;static char is_safe[UCHAR_MAX + 1];/* * Convert a pointer to a C string literal to a long that can be used * for hashing. This is done by converting the pointer into an integer * and discarding the lowest to bits to get rid of typical alignment * bits. */#define CSTR_TO_LONG(ptr) (((long) ptr) >> 2)/*********************************************************************** * Declarations of internal functions. These are defined at the end of * the file. */static void seems_valid_real(Octstr *ostr, const char *filename, long lineno, const char *function);#ifdef NO_GWASSERT#define seems_valid(ostr)#else#define seems_valid(ostr) \ (seems_valid_real(ostr, __FILE__, __LINE__, __func__))#endif/*********************************************************************** * Implementations of the functions declared in octstr.h. See the * header for explanations of what they should do. *//* Reserve space for at least 'size' octets */static void octstr_grow(Octstr *ostr, long size){ gw_assert(!ostr->immutable); seems_valid(ostr); gw_assert(size >= 0); size++; /* make room for the invisible terminating NUL */ if (size > ostr->size) { ostr->data = gw_realloc(ostr->data, size); ostr->size = size; }}/* * Fill is_safe table. is_safe[c] means that c can be left as such when * url-encoded. * RFC 2396 defines the list of characters that need to be encoded. * Space is treated as an exception by the encoding routine; * it's listed as safe here, but is actually changed to '+'. */static void urlcode_init(void){ int i; unsigned char *safe = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz-_.!~*'()"; for (i = 0; safe[i] != '\0'; ++i) is_safe[safe[i]] = 1;}void octstr_init(void){ urlcode_init(); mutex_init_static(&immutables_mutex); immutables_init = 1;}void octstr_shutdown(void){ long i, n; n = 0; for (i = 0; i < MAX_IMMUTABLES; ++i) { if (immutables[i] != NULL) { gw_free(immutables[i]); ++n; } } debug("gwlib.octstr", 0, "Immutable octet strings: %ld.", n); mutex_destroy(&immutables_mutex);}Octstr *octstr_create_real(const char *cstr){ gw_assert(cstr != NULL); return octstr_create_from_data(cstr, strlen(cstr));}Octstr *octstr_create_from_data_real(const char *data, long len){ Octstr *ostr; gw_assert(len >= 0); if (data == NULL) gw_assert(len == 0); ostr = gw_malloc(sizeof(*ostr)); if (len == 0) { ostr->len = 0; ostr->size = 0; ostr->data = NULL; } else { ostr->len = len; ostr->size = len + 1; ostr->data = gw_malloc(ostr->size); memcpy(ostr->data, data, len); ostr->data[len] = '\0'; } ostr->immutable = 0; seems_valid(ostr); return ostr;}Octstr *octstr_imm(const char *cstr){ Octstr *os; long i, index; unsigned char *data; gw_assert(immutables_init); gw_assert(cstr != NULL); index = CSTR_TO_LONG(cstr) % MAX_IMMUTABLES; data = (unsigned char *) cstr; mutex_lock(&immutables_mutex); i = index; for (; ; ) { if (immutables[i] == NULL || immutables[i]->data == data) break; i = (i + 1) % MAX_IMMUTABLES; if (i == index) panic(0, "Too many immutable strings."); } os = immutables[i]; if (os == NULL) { /* * Can't use octstr_create() because it copies the string, * which would break our hashing. */ os = gw_malloc(sizeof(*os)); os->data = data; os->len = strlen(data); os->size = os->len + 1; os->immutable = 1; immutables[i] = os; seems_valid(os); } mutex_unlock(&immutables_mutex); return os;}void octstr_destroy_impl(Octstr *ostr, const char *file, long line, const char *func){ if (ostr != NULL) { seems_valid_real(ostr, file, line, func); if (!ostr->immutable) { gw_free(ostr->data); gw_free(ostr); } }}void octstr_destroy_item(void *os){ octstr_destroy(os);}long octstr_len(Octstr *ostr){ if (ostr == NULL) return 0; seems_valid(ostr); return ostr->len;}Octstr *octstr_copy_real(Octstr *ostr, long from, long len){ seems_valid(ostr); gw_assert(from >= 0); gw_assert(len >= 0); if (from >= ostr->len) return octstr_create(""); if (len > ostr->len - from) len = ostr->len - from; return octstr_create_from_data(ostr->data + from, len);}Octstr *octstr_duplicate_real(Octstr *ostr){ if (ostr == NULL) return NULL; seems_valid(ostr); return octstr_create_from_data(ostr->data, ostr->len);}Octstr *octstr_cat(Octstr *ostr1, Octstr *ostr2){ Octstr *ostr; seems_valid(ostr1); seems_valid(ostr2); gw_assert(!ostr1->immutable); ostr = octstr_create(""); ostr->len = ostr1->len + ostr2->len; ostr->size = ostr->len + 1; ostr->data = gw_malloc(ostr->size); if (ostr1->len > 0) memcpy(ostr->data, ostr1->data, ostr1->len); if (ostr2->len > 0) memcpy(ostr->data + ostr1->len, ostr2->data, ostr2->len); ostr->data[ostr->len] = '\0'; seems_valid(ostr); return ostr;}int octstr_get_char(Octstr *ostr, long pos){ seems_valid(ostr); if (pos >= ostr->len || pos < 0) return -1; return ostr->data[pos];}void octstr_set_char(Octstr *ostr, long pos, int ch){ seems_valid(ostr); gw_assert(!ostr->immutable); if (pos < ostr->len) ostr->data[pos] = ch; seems_valid(ostr);}void octstr_get_many_chars(char *buf, Octstr *ostr, long pos, long len){ gw_assert(buf != NULL); seems_valid(ostr); if (pos >= ostr->len) return; if (pos + len > ostr->len) len = ostr->len - pos; if (len > 0) memcpy(buf, ostr->data + pos, len);}char *octstr_get_cstr_real(Octstr *ostr, const char *file, long line, const char *func){ seems_valid_real(ostr, file, line, func); if (ostr->len == 0) return ""; return ostr->data;}void octstr_append_from_hex(Octstr *ostr, char *hex){ Octstr *output; seems_valid(ostr); gw_assert(!ostr->immutable); output = octstr_create(hex); octstr_hex_to_binary(output); octstr_append(ostr, output); octstr_destroy(output);}void octstr_binary_to_hex(Octstr *ostr, int uppercase){ unsigned char *hexits; long i; seems_valid(ostr); gw_assert(!ostr->immutable); if (ostr->len == 0) return; hexits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; octstr_grow(ostr, ostr->len * 2); /* In-place modification must be done back-to-front to avoid * overwriting the data while we read it. Even the order of * the two assignments is important, to get i == 0 right. */ for (i = ostr->len - 1; i >= 0; i--) { ostr->data[i * 2 + 1] = hexits[ostr->data[i] % 16]; ostr->data[i * 2] = hexits[(ostr->data[i] / 16) & 0xf]; } ostr->len = ostr->len * 2; ostr->data[ostr->len] = '\0'; seems_valid(ostr);}int octstr_hex_to_binary(Octstr *ostr){ long len, i; unsigned char *p; seems_valid(ostr); gw_assert(!ostr->immutable); if (ostr->len == 0) return 0; /* Check if it's in the right format */ if (!octstr_check_range(ostr, 0, ostr->len, gw_isxdigit)) return -1; len = ostr->len; /* Convert ascii data to binary values */ for (i = 0, p = ostr->data; i < len; i++, p++) { if (*p >= '0' && *p <= '9') *p -= '0'; else if (*p >= 'a' && *p <= 'f') *p = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') *p = *p - 'A' + 10; else { /* isxdigit checked the whole string, so we should * not be able to get here. */ gw_assert(0); *p = 0; } } /* De-hexing will compress data by factor of 2 */ len = ostr->len / 2; for (i = 0; i < len; i++) { ostr->data[i] = ostr->data[i * 2] * 16 | ostr->data[i * 2 + 1]; } ostr->len = len; ostr->data[len] = '\0'; seems_valid(ostr); return 0;}void octstr_binary_to_base64(Octstr *ostr){ static const unsigned char base64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; long triplets; long lines; long orig_len; unsigned char *data; long from, to; int left_on_line; seems_valid(ostr); gw_assert(!ostr->immutable); if (ostr->len == 0) { /* Always terminate with CR LF */ octstr_insert(ostr, octstr_imm("\015\012"), 0); return; } /* The lines must be 76 characters each (or less), and each * triplet will expand to 4 characters, so we can fit 19 * triplets on one line. We need a CR LF after each line, * which will add 2 octets per 19 triplets (rounded up). */ triplets = (ostr->len + 2) / 3; /* round up */ lines = (triplets + 18) / 19; octstr_grow(ostr, triplets * 4 + lines * 2); orig_len = ostr->len; data = ostr->data; ostr->len = triplets * 4 + lines * 2; data[ostr->len] = '\0'; /* This function works back-to-front, so that encoded data will * not overwrite source data. * from points to the start of the last triplet (which may be * an odd-sized one), and to points to the start of where the * last quad should go. */ from = (triplets - 1) * 3; to = (triplets - 1) * 4 + (lines - 1) * 2; /* First write the CR LF after the last quad */ data[to + 5] = 10; /* LF */ data[to + 4] = 13; /* CR */ left_on_line = triplets - ((lines - 1) * 19); /* base64 encoding is in 3-octet units. To handle leftover * octets, conceptually we have to zero-pad up to the next * 6-bit unit, and pad with '=' characters for missing 6-bit * units. * We do it by first completing the first triplet with * zero-octets, and after the loop replacing some of the * result characters with '=' characters. * There is enough room for this, because even with a 1 or 2 * octet source string, space for four octets of output * will be reserved. */ switch (orig_len % 3) { case 0: break; case 1: data[orig_len] = 0; data[orig_len + 1] = 0; break; case 2: data[orig_len + 1] = 0; break; } /* Now we only have perfect triplets. */ while (from >= 0) { long whole_triplet; /* Add a newline, if necessary */ if (left_on_line == 0) { to -= 2; data[to + 5] = 10; /* LF */ data[to + 4] = 13; /* CR */ left_on_line = 19; } whole_triplet = (data[from] << 16) | (data[from + 1] << 8) | data[from + 2]; data[to + 3] = base64[whole_triplet % 64]; data[to + 2] = base64[(whole_triplet >> 6) % 64]; data[to + 1] = base64[(whole_triplet >> 12) % 64]; data[to] = base64[(whole_triplet >> 18) % 64]; to -= 4; from -= 3; left_on_line--; } gw_assert(left_on_line == 0); gw_assert(from == -3); gw_assert(to == -4); /* Insert padding characters in the last quad. Remember that * there is a CR LF between the last quad and the end of the * string. */ switch (orig_len % 3) { case 0: break; case 1: gw_assert(data[ostr->len - 3] == 'A'); gw_assert(data[ostr->len - 4] == 'A'); data[ostr->len - 3] = '='; data[ostr->len - 4] = '='; break; case 2: gw_assert(data[ostr->len - 3] == 'A'); data[ostr->len - 3] = '='; break; } seems_valid(ostr);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -