From 8f922d174e08026120363533dbf48349266ee7cb Mon Sep 17 00:00:00 2001 From: Graham Miller Date: Fri, 14 Jan 2011 13:40:52 -0500 Subject: Added tag 0.1 for changeset 74f74c375a95 --- LICENSE | 25 +++ Makefile | 44 +++++ encode.c | 304 +++++++++++++++++++++++++++++ encode.h | 609 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ encode_test.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ encode_test.h | 13 ++ gob.h | 22 +++ test_main.c | 58 ++++++ 8 files changed, 1668 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 encode.c create mode 100644 encode.h create mode 100644 encode_test.c create mode 100644 encode_test.h create mode 100644 gob.h create mode 100644 test_main.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b73cd0f --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2011, The libgob Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the libgob Authors nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b4a986 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# source files. +SRC = encode.c +TEST_SRC = test_main.c encode_test.c + +OBJ = $(SRC:.c=.o) +TEST_OBJ = $(TEST_SRC:.c=.o) + +OUT = libgob.a + +# include directories +INCLUDES = -I. -I/usr/local/include + +# C++ compiler flags (-g -O2 -Wall) +CCFLAGS = -g + +# compiler +CC = gcc + +# library paths +LIBS = -L../ -L/usr/local/lib -lm + +# compile flags +LDFLAGS = -g + +CUNIT_LDFLAGS= -lcunit + +.SUFFIXES: .c + +default: $(OUT) + +.c.o: + $(CC) $(INCLUDES) $(CCFLAGS) -c $< -o $@ + +$(OUT): $(OBJ) + ar rcs $(OUT) $(OBJ) + +clean: + rm -f $(OBJ) $(TEST_OBJ) $(OUT) Makefile.bak + +test: $(OBJ) $(TEST_OBJ) + $(CC) $^ -o $@ -lm $(CUNIT_LDFLAGS) + +exe: $(OUT) main.o + $(CC) $^ -o $@ -lm -lgob -L. $(LDFLAGS) \ No newline at end of file diff --git a/encode.c b/encode.c new file mode 100644 index 0000000..687dbfe --- /dev/null +++ b/encode.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include + +#include "gob.h" +#include "encode.h" + +static int sNextTypeId = 65; + +unsigned long long flip_unsigned_long_long(unsigned long long ull) { + ull = ((ull >> 8) & 0x00FF00FF00FF00FF) | ((ull & 0x00FF00FF00FF00FF) << 8 ); + ull = ((ull >> 16) & 0x0000FFFF0000FFFF) | ((ull & 0x0000FFFF0000FFFF) << 16 ); + ull = ((ull >> 32) ) | ((ull ) << 32 ); + return ull; +} + +int gob_allocate_type_id() { + return sNextTypeId++; +} + +// a return value of buf_size or more means that output +// was truncated. +int gob_encode_unsigned_long_long(char *buf, size_t buf_size, unsigned long long ull) { + if (ull < 128) { + if (buf_size >= 1) { + *buf = (char)ull; + return 1; + } + } + unsigned char *ull_ptr = (char*)&ull; + unsigned char *end_ptr = ull_ptr + (sizeof(unsigned long long)-1); + char *write_ptr = buf + 1; + int bytes_to_write = 1; + int seen_first_bit = 0; + // high byte first + while (end_ptr >= ull_ptr) { + if (*end_ptr != 0 || seen_first_bit) { + seen_first_bit = 1; + bytes_to_write++; + if (bytes_to_write <= buf_size) { + *write_ptr = *end_ptr; + write_ptr++; + } + } + end_ptr--; + } + if (buf_size >= 1) { + *buf = -1*((char)bytes_to_write-1); // byte count omits first byte + } + return bytes_to_write; +} + +int gob_encode_unsigned_int(char *buf, size_t buf_size, unsigned int i) { + return gob_encode_unsigned_long_long(buf, buf_size, (unsigned long long)i); +} + +int gob_encode_long_long(char *buf, size_t buf_size, long long i) { + unsigned long long u; + if (i < 0) { + u = (~i << 1) | 1; // complement i, bit 0 is 1 + } else { + u = (i << 1); // do not complement i, bit 0 is 0 + } + return gob_encode_unsigned_long_long(buf, buf_size, u); +} + +int gob_encode_int(char *buf, size_t buf_size, int i) { + unsigned int u; + if (i < 0) { + u = (~i << 1) | 1; // complement i, bit 0 is 1 + } else { + u = (i << 1); // do not complement i, bit 0 is 0 + } + return gob_encode_unsigned_int(buf, buf_size, u); +} + +int gob_encode_boolean(char *buf, size_t buf_size, int b) { + return gob_encode_unsigned_long_long(buf, buf_size, b != 0); +} + +int gob_encode_double(char *buf, size_t buf_size, double d) { + return gob_encode_unsigned_long_long(buf, buf_size, flip_unsigned_long_long(*(unsigned long long*)&d)); +} + +int gob_encode_string(char *buf, size_t buf_size, const char *s) { + size_t len = strlen(s); + int encoded_len_size = gob_encode_unsigned_int(buf, buf_size, len); + buf += encoded_len_size; + buf_size -= encoded_len_size; + if (buf_size > 0) { + strncpy(buf, s, buf_size); + } + return len + encoded_len_size; +} + +int gob_start_type_definition(char *buf, size_t buf_size, int id, int type) { + int total_size = 0; + int num_bytes = 0; + char *write_ptr = buf; + + num_bytes = gob_start_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + num_bytes = gob_encode_int(write_ptr, buf_size, -1*id); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + int type_delta = 0; + switch (type) { + case GOB_ARRAYTYPE_ID: + type_delta = 1; + break; + case GOB_SLICETYPE_ID: + type_delta = 2; + break; + case GOB_STRUCTTYPE_ID: + default: + type_delta = 3; + break; + case GOB_MAPTYPE_ID: + type_delta = 4; + break; + } + + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, type_delta); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + return total_size; +} + +int gob_end_type_definition(char *buf, size_t buf_size) { + // just the end of the wireType struct + return gob_end_struct(buf, buf_size); +} + +int gob_start_struct_type(char *buf, size_t buf_size, const char *name, int id) { + int total_size = 0; + int num_bytes = 0; + char *write_ptr = buf; + + num_bytes = gob_start_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, 1); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + num_bytes = gob_encode_common_type(write_ptr, buf_size, name, id); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + return total_size; +} + +int gob_end_struct_type(char *buf, size_t buf_size) { + // just the end of the wireType struct + return gob_end_struct(buf, buf_size); +} + +int gob_encode_string_int_helper(char *buf, size_t buf_size, const char *name, int id) { + int total_size = 0; + int num_bytes = 0; + char *write_ptr = buf; + + num_bytes = gob_start_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + int fieldDelta = 1; + if (name == NULL || *name == '\0') { // in go, null and "" are the same + fieldDelta ++; + } else { + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, fieldDelta); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + num_bytes = gob_encode_string(write_ptr, buf_size, name); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + } + if (id != 0) { + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, fieldDelta); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + num_bytes = gob_encode_int(write_ptr, buf_size, id); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + } + + num_bytes = gob_end_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + return total_size; +} + +int gob_encode_common_type(char *buf, size_t buf_size, const char *name, int id) { + return gob_encode_string_int_helper(buf, buf_size, name, id); +} + +int gob_encode_field_type(char *buf, size_t buf_size, const char *name, int id) { + return gob_encode_string_int_helper(buf, buf_size, name, id); +} + +int gob_encode_array_type(char *buf, size_t buf_size, const char *name, int id, int elem_type, int len) { + int total_size = 0; + char *write_ptr = buf; + int num_bytes = gob_start_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, 1); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + num_bytes = gob_encode_common_type(write_ptr, buf_size, name, id); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + int field_delta = 1; + if (elem_type == 0) { + field_delta++; + } else { + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, field_delta); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + num_bytes = gob_encode_int(write_ptr, buf_size, elem_type); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + } + + if (len == 0) { + field_delta++; + } else { + num_bytes = gob_encode_unsigned_int(write_ptr, buf_size, field_delta); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + num_bytes = gob_encode_int(write_ptr, buf_size, len); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + } + + num_bytes = gob_end_struct(write_ptr, buf_size); + write_ptr += num_bytes; + buf_size -= num_bytes; + total_size += num_bytes; + + return total_size; +} + +int gob_encode_slice_type(char *buf, size_t buf_size, const char *name, int id, int elem_type) { + // array type will not output len if it is 0 + return gob_encode_array_type(buf, buf_size, name, id, elem_type, 0); +} + +int gob_start_array(char *buf, size_t buf_size, size_t size) { + return gob_encode_unsigned_int(buf, buf_size, size); +} + +int gob_end_array(char *buf, size_t buf_size) { + return 0; +} + +int gob_start_slice(char *buf, size_t buf_size, size_t size) { + return gob_encode_unsigned_int(buf, buf_size, size); +} + +int gob_end_slice(char *buf, size_t buf_size) { + return 0; +} + +int gob_start_struct(char *buf, size_t buf_size) { + return 0; +} + +int gob_end_struct(char *buf, size_t buf_size) { + if (buf_size >= 1) { + *buf = '\0'; + } + return 1; +} + diff --git a/encode.h b/encode.h new file mode 100644 index 0000000..e3f15f8 --- /dev/null +++ b/encode.h @@ -0,0 +1,609 @@ +#ifndef _ENCODE_H +#define _ENCODE_H + +#include + +/** + * Allocates a new type ID. + * + * Note that this method is not thread-safe. + * + * @return + * An identifier for the representation of a type in a gob stream. + */ +int gob_allocate_type_id(); + +/////////////////////////////////////////////////////////////////////////////// +// Basic Types + +/** + * Encodes an unsigned long long into the specified buffer. + * + * From the gob package documentation: "An unsigned integer is sent one of two + * ways. If it is less than 128, it is sent as a byte with that value. + * Otherwise it is sent as a minimal-length big-endian (high byte first) byte + * stream holding the value, preceded by one byte holding the byte count, + * negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and 256 + * is transmitted as (FE 01 00). " + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param ull + * The number to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_unsigned_long_long(char *buf, size_t buf_size, unsigned long long ull); + +/** + * Encodes an unsigned int into the specified buffer. + * + * From the gob package documentation: "An unsigned integer is sent one of two + * ways. If it is less than 128, it is sent as a byte with that value. + * Otherwise it is sent as a minimal-length big-endian (high byte first) byte + * stream holding the value, preceded by one byte holding the byte count, + * negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and 256 + * is transmitted as (FE 01 00). " + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param i + * The number to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_unsigned_int(char *buf, size_t buf_size, unsigned int i); + +/** + * Encodes an int into the specified buffer. + * + * From the gob package documentation: "A signed integer, i, is encoded within + * an unsigned integer, u. Within u, bits 1 upward contain the value; bit 0 + * says whether they should be complemented upon receipt. The encode algorithm + * looks like this: + * + * \code + * uint u; + * if i < 0 { + * u = (^i << 1) | 1 // complement i, bit 0 is 1 + * } else { + * u = (i << 1) // do not complement i, bit 0 is 0 + * } + * encodeUnsigned(u) + * \endcode + * " + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param i + * The number to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_int(char *buf, size_t buf_size, int i); + +/** + * Encodes a long long into the specified buffer. + * + * From the gob package documentation: "A signed integer, i, is encoded within + * an unsigned integer, u. Within u, bits 1 upward contain the value; bit 0 + * says whether they should be complemented upon receipt. The encode algorithm + * looks like this: + * + * \code + * uint u; + * if i < 0 { + * u = (^i << 1) | 1 // complement i, bit 0 is 1 + * } else { + * u = (i << 1) // do not complement i, bit 0 is 0 + * } + * encodeUnsigned(u) + * \endcode + * " + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param i + * The number to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_long_long(char *buf, size_t buf_size, long long i); + +/** + * Encodes a boolean into the specified buffer. + * + * From the gob package documentation: "A boolean is encoded within an unsigned + * integer: 0 for false, 1 for true." + * + * The function takes an int argument and writes false to the stream if + * the int is 0, writes true otherwise. + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param b + * The boolean to encode, 0 for false, otherwise true + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_boolean(char *buf, size_t buf_size, int b); + +/** + * Encodes a boolean into the specified buffer. + * + * From the gob package documentation: "Floating-point numbers are always sent + * as a representation of a float64 value. That value is converted to a uint64 + * using math.Float64bits. The uint64 is then byte-reversed and sent as a + * regular unsigned integer. The byte-reversal means the exponent and + * high-precision part of the mantissa go first. Since the low bits are often + * zero, this can save encoding bytes. For instance, 17.0 is encoded in only + * three bytes (FE 31 40)." + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param d + * The double to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_double(char *buf, size_t buf_size, double d); + +/** + * Encodes a boolean into the specified buffer. + * + * From the gob package documentation: "Strings and slices of bytes are sent as + * an unsigned count followed by that many uninterpreted bytes of the value. " + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param s + * A zero-terminated (C-style) string to encode + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_encode_string(char *buf, size_t buf_size, const char *s); + +/////////////////////////////////////////////////////////////////////////////// +// More complex built-in types + +/** + * Provides the prefix of the array encoding. + * + * From the gob package documentation: "All other slices and arrays are sent as + * an unsigned count followed by that many elements using the standard gob + * encoding for their type, recursively." + * + * This method simply encodes the array count. It is the responsibility of the + * user to encode the appropriate number of elements before a call to + * gob_end_array() + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param size + * The number of elements in the array to follow + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_start_array(char *buf, size_t buf_size, size_t size); + +/** + * Provides the suffix of the array encoding. + * + * From the gob package documentation: "All other slices and arrays are sent as + * an unsigned count followed by that many elements using the standard gob + * encoding for their type, recursively." + * + * Note that this method currently always returns 0, but is provided for + * symmetry with gob_start_array(). + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_end_array(char *buf, size_t buf_size); + +/** + * Provides the prefix of the slice encoding. + * + * From the gob package documentation: "All other slices and arrays are sent as + * an unsigned count followed by that many elements using the standard gob + * encoding for their type, recursively." + * + * This method simply encodes the slice count. It is the responsibility of the + * user to encode the appropriate number of elements before a call to + * gob_end_array() + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param size + * The number of elements in the slice to follow + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_start_slice(char *buf, size_t buf_size, size_t size); + +/** + * Provides the suffix of the slice encoding. + * + * From the gob package documentation: "All other slices and arrays are sent as + * an unsigned count followed by that many elements using the standard gob + * encoding for their type, recursively." + * + * Note that this method currently always returns 0, but is provided for + * symmetry with gob_start_slice(). + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_end_slice(char *buf, size_t buf_size); + +/** + * Provides the prefix of the struct encoding. + * + * From the gob package documentation: "Structs are sent as a sequence of + * (field number, field value) pairs. The field value is sent using the + * standard gob encoding for its type, recursively. If a field has the zero + * value for its type, it is omitted from the transmission. The field + * number is defined by the type of the encoded struct: the first field of + * the encoded type is field 0, the second is field 1, etc. ... Finally, + * after all the fields have been sent a terminating mark denotes the end + * of the struct. That mark is a delta=0 value, which has representation (00)." + * + * Note that this method currently always returns 0, but is provided for + * symmetry with gob_end_struct(). + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_start_struct(char *buf, size_t buf_size); + +/** + * Provides the suffix of the struct encoding. + * + * From the gob package documentation: "Structs are sent as a sequence of + * (field number, field value) pairs. The field value is sent using the + * standard gob encoding for its type, recursively. If a field has the zero + * value for its type, it is omitted from the transmission. The field + * number is defined by the type of the encoded struct: the first field of + * the encoded type is field 0, the second is field 1, etc. ... Finally, + * after all the fields have been sent a terminating mark denotes the end + * of the struct. That mark is a delta=0 value, which has representation (00)." + * + * This method simply encodes the 00 delta value. + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_end_struct(char *buf, size_t buf_size); + +/////////////////////////////////////////////////////////////////////////////// +// Type declarations + +/** + * Encodes the prefix of a type definition. + * + * From the gob package documentation: "To define a type, the encoder chooses + * an unused, positive type id and sends the pair (-type id, encoded-type) + * where encoded-type is the gob encoding of a wireType description, + * constructed from these types ..." + * + * This method encodes the type id and the offset into the wireType struct. + * Note that the definition of wireType differs from that in the documentation + * in that it has several fields, of which it appears only one ever has + * a value. + * + * /code + * type wireType struct { + * arrayT *arrayType + * sliceT *sliceType + * structT *structType + * mapT *mapType + * } + * /endcode + * + * The client is responsible for encoding the value of an arrayT sliceT + * structT or mapT, before a call to gob_end_type_definition() + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param id + * The (positive) type id, as returned from gob_allocate_type_id() + * @param type + * One of GOB_ARRAYTYPE_ID, GOB_SLICETYPE_ID, GOB_STRUCTTYPE_ID, + * GOB_MAPTYPE_ID + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_start_type_definition(char *buf, size_t buf_size, int id, int type); + +/** + * Encodes the suffix of a type definition. + * + * From the gob package documentation: "To define a type, the encoder chooses + * an unused, positive type id and sends the pair (-type id, encoded-type) + * where encoded-type is the gob encoding of a wireType description, + * constructed from these types ..." + * + * This method simply calls gob_end_struct() to end the wireType struct + * definition. + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_end_type_definition(char *buf, size_t buf_size); + +/** + * Encodes the prefix of a structType definition. + * + * This method encodes the prefix of a structType, including the + * entire commonType, where the definitions are as follows: + * + * /code + * type commonType { + * name string // the name of the struct type + * _id int // the id of the type, repeated for so it's inside the type + * } + * + * type structType struct { + * commonType + * field []*fieldType // the fields of the struct. + * } + * /endcode + * + * The client is responsible for encoding the field slice before a call to + * gob_end_struct_type(). + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param name + * A zero-terminated (C-style) string representing the name of the struct + * type. + * @param type + * The id of the type as returned by gob_allocate_type_id() + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_start_struct_type(char *buf, size_t buf_size, const char *name, int id); + +/** + * Encodes the suffix of a structType definition. + * + * This method simply calls gob_end_struct() to end the structType struct. + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * + * @return + * The number of bytes that would have been written by the encode operation. + * A return value greater than buf_size indicates a partial encode has + * occurred (buffer overflow). + */ +int gob_end_struct_type(char *buf, size_t buf_size); + +/** + * Encodes the commonType struct (usually should not be called explicitly). + * + * This method encodes an entire instance of the commonType struct. Because + * this struct is part of the prefix of other types, it typically is called + * only indirectly through a method like gob_start_struct_type(). + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param name + * A zero-terminated (C-style) string representing the name of the struct + * type. + * @param id + * The id of the type as returned by gob_allocate_type_id() + */ +int gob_encode_common_type(char *buf, size_t buf_size, const char *name, int id); + +/** + * Encodes the fieldType struct. + * + * This method encodes an entire instance of the fieldType struct, as defined: + * + * /code + * type fieldType struct { + * name string // the name of the field. + * id int // the type id of the field, which must be already defined + * } + * /endcode + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param name + * A zero-terminated (C-style) string representing the name of the struct + * type. + * @param id + * The id of the type either returned by gob_allocate_type_id() or as defined + * in gob.h + */ +int gob_encode_field_type(char *buf, size_t buf_size, const char *name, int id); + +/** + * Encodes the arrayType struct. + * + * This method encodes an entire instance of the arrayType struct, as defined: + * + * /code + * type commonType { + * name string // the name of the struct type + * _id int // the id of the type, repeated for so it's inside the type + * } + * + * type arrayType struct { + * commonType + * Elem typeId + * Len int + * } + * /endcode + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param name + * A zero-terminated (C-style) string representing the name of the array + * type. + * @param id + * The id of the array type itself, as returned by gob_allocate_type_id() + * @param type_id + * The id of the type of the elements of the array, as returned by + * gob_allocate_type_id() or as defined in gob.h. + * @param len + * The length of the array. + * + */ +int gob_encode_array_type(char *buf, size_t buf_size, const char *name, int id, int type_id, int len); + +/** + * Encodes the sliceType struct. + * + * This method encodes an entire instance of the sliceType struct, as defined: + * + * /code + * type commonType { + * name string // the name of the struct type + * _id int // the id of the type, repeated for so it's inside the type + * } + * + * type sliceType struct { + * commonType + * Elem typeId + * } + * /endcode + * + * @param buf + * The buffer into which to encode the given number. The pointer must point + * to "empty" space in the buffer. + * @param buf_size + * The number of bytes in buf available for writing + * @param name + * A zero-terminated (C-style) string representing the name of the array + * type. + * @param id + * The id of the slice type itself, as returned by gob_allocate_type_id() + * @param type_id + * The id of the type of the elements of the slice, as returned by + * gob_allocate_type_id() or as defined in gob.h. + * + */ +int gob_encode_slice_type(char *buf, size_t buf_size, const char *name, int id, int elem_type); + +#endif + diff --git a/encode_test.c b/encode_test.c new file mode 100644 index 0000000..6b70a81 --- /dev/null +++ b/encode_test.c @@ -0,0 +1,593 @@ +#include "CUnit/Basic.h" +#include "CUnit/Console.h" +#include "CUnit/Automated.h" + +#include "gob.h" +#include "encode.h" +#include + +unsigned long long flip_unsigned_long_long(unsigned long long ull); + +void test_gob_encode_unsigned_int() +{ + char buf[1024]; + int num_bytes = gob_encode_unsigned_int(buf, 1024, 127); + CU_ASSERT_EQUAL(1, num_bytes); + CU_ASSERT_EQUAL(127, buf[0]); + + num_bytes = gob_encode_unsigned_int(buf, 1024, 0); + CU_ASSERT_EQUAL(1, num_bytes); + CU_ASSERT_EQUAL((char)0, buf[0]); + + num_bytes = gob_encode_unsigned_int(buf, 1024, 7); + CU_ASSERT_EQUAL(1, num_bytes); + CU_ASSERT_EQUAL((char)7, buf[0]); + + + num_bytes = gob_encode_unsigned_int(buf, 1024, 129); + CU_ASSERT_EQUAL(2, num_bytes); + CU_ASSERT_EQUAL((char)-1, buf[0]); + CU_ASSERT_EQUAL((char)129, buf[1]); + + num_bytes = gob_encode_unsigned_int(buf, 1024, 256); + CU_ASSERT_EQUAL(3, num_bytes); + CU_ASSERT_EQUAL((char)-2, buf[0]); + CU_ASSERT_EQUAL((char)1, buf[1]); + CU_ASSERT_EQUAL((char)0, buf[2]); + + num_bytes = gob_encode_unsigned_int(buf, 1024, 0x6ABCDEF0); + CU_ASSERT_EQUAL(5, num_bytes); + CU_ASSERT_EQUAL((char)-4, buf[0]); + CU_ASSERT_EQUAL((char)0x6A, buf[1]); + CU_ASSERT_EQUAL((char)0xBC, buf[2]); + CU_ASSERT_EQUAL((char)0xDE, buf[3]); + CU_ASSERT_EQUAL((char)0xF0, buf[4]); + + num_bytes = gob_encode_unsigned_int(buf, 1024, 0xFFFFFFFF); + CU_ASSERT_EQUAL(5, num_bytes); + CU_ASSERT_EQUAL((char)-4, buf[0]); + CU_ASSERT_EQUAL((char)0xFF, buf[1]); + CU_ASSERT_EQUAL((char)0xFF, buf[2]); + CU_ASSERT_EQUAL((char)0xFF, buf[3]); + CU_ASSERT_EQUAL((char)0xFF, buf[4]); + + + + // test buffer too short + buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = 0xFF; + num_bytes = gob_encode_unsigned_int(buf, 3, 0x6ABCDEF0); + CU_ASSERT_EQUAL(5, num_bytes); + CU_ASSERT_EQUAL((char)-4, buf[0]); + CU_ASSERT_EQUAL((char)0x6A, buf[1]); + CU_ASSERT_EQUAL((char)0xBC, buf[2]); + CU_ASSERT_EQUAL((char)0xFF, buf[3]); + CU_ASSERT_EQUAL((char)0xFF, buf[4]); + +} + +void test_flip_unsigned_long_long(){ + unsigned long long result = flip_unsigned_long_long(0xABCDEF0123456789); + CU_ASSERT_EQUAL(0x8967452301EFCDAB, result); + result = flip_unsigned_long_long(0xA8B278C47816374A); + CU_ASSERT_EQUAL(0x4A371678C478B2A8, result); +} + +void test_gob_encode_double() { + char buf[1024]; + int num_bytes = gob_encode_double(buf, 1024, 17.0); + CU_ASSERT_EQUAL(3, num_bytes); + CU_ASSERT_EQUAL((char)0xFE, buf[0]); + CU_ASSERT_EQUAL((char)0x31, buf[1]); + CU_ASSERT_EQUAL((char)0x40, buf[2]); + +} +void test_gob_encode_int() +{ + char buf[1024]; + memset(buf, '\0', 1024); + + int num_bytes = gob_encode_int(buf, 1024, 1); + CU_ASSERT_EQUAL(1, num_bytes); + CU_ASSERT_EQUAL((char)0x02, buf[0]); + + num_bytes = gob_encode_int(buf, 1024, -1); + CU_ASSERT_EQUAL(1, num_bytes); + CU_ASSERT_EQUAL((char)0x01, buf[0]); + + num_bytes = gob_encode_int(buf, 1024, 1000); + CU_ASSERT_EQUAL(3, num_bytes); + CU_ASSERT_EQUAL((char)0xFE, buf[0]); + CU_ASSERT_EQUAL((char)0x07, buf[1]); + CU_ASSERT_EQUAL((char)0xD0, buf[2]); + + num_bytes = gob_encode_int(buf, 1024, -1000); + CU_ASSERT_EQUAL(3, num_bytes); + CU_ASSERT_EQUAL((char)0xFE, buf[0]); + CU_ASSERT_EQUAL((char)0x07, buf[1]); + CU_ASSERT_EQUAL((char)0xcf, buf[2]); + +} + +void test_gob_encode_string() { + char buf[1024]; + char *str = "I love unit tests!"; + int num_bytes = gob_encode_string(buf, 1024, str); + CU_ASSERT_EQUAL((char)18, buf[0]); + CU_ASSERT(memcmp(str, buf+1, strlen(str))==0); + + // test buffer too small + memset(buf, 0, 1024); + num_bytes = gob_encode_string(buf, 10, str); + CU_ASSERT_EQUAL((char)18, buf[0]); + CU_ASSERT_EQUAL((char)0, buf[10]); + CU_ASSERT(memcmp(str, buf+1, 9) == 0); +} + +void test_gob_encode_simple_type() { + // Encodes this type and a value: + // type MyType struct { + // Name string + //} + // + + + char buf[1024]; + memset(buf, '\0', 1024); + int num_bytes = 0; + int total_bytes = 0; + char *write_ptr = buf; + + int type_id = 65; + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 29); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_type_definition(write_ptr, 1024-total_bytes, type_id, GOB_STRUCTTYPE_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_struct_type(write_ptr, 1024-total_bytes, "MyType", type_id); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // field delta for field ([]*fieldType) + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send slice size + num_bytes = gob_start_slice(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send field type + num_bytes = gob_encode_field_type(write_ptr, 1024-total_bytes, "Name", GOB_STRING_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end slice + num_bytes = gob_end_slice(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end struct type + num_bytes = gob_end_struct_type(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end MyType + num_bytes = gob_end_type_definition(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + /////////////////////////////////////////////////// + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 10); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_int(write_ptr, 1024-total_bytes, type_id); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_string(write_ptr, 1024-total_bytes, "hello"); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end MyType + num_bytes = gob_end_struct(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + + + char result_buf[] = { + 0x1d, // message length of 29 + 0xff, 0x81,// type id 65 (negated) + 0x03, // field delta for s structType + 0x01, // field delta for commonType + 0x01, // field delta for name string + 0x06, // string length of 6 + 0x4d, 0x79, 0x54, 0x79, 0x70, 0x65, // "MyType" + 0x01, // field delta for _id int + 0xff, 0x82, // type id 65 + 0x00, // end of commonType? + 0x01, // field delta for field + 0x01, // length of field array + 0x01, // field delta of name string + 0x04, // length of string + 0x4e, 0x61, 0x6d, 0x65, // "Name" + 0x01, // field delta of id + 0x0c, // type id + 0x00, // end fieldType + 0x00, // end structType + 0x00, // end wireType + 0x0a, // message length of 10 + 0xff, 0x82,// type id 65 + 0x01, // field delta for name string + 0x05, // string length + 0x68, 0x65, 0x6c, 0x6c, 0x6f, // "hello" + 0x00 // end MyType + }; + + CU_ASSERT_EQUAL(41, total_bytes); + CU_ASSERT(memcmp(result_buf, buf, total_bytes) == 0); + + +} + + +void test_gob_encode_more_complex_type() { + + //Encodes the following types and a value: + // + //type FieldData struct { + // fFloat float64 + // iInt int + //} + // + //type MyData struct { + // MyName string + // Fields []FieldData + //} + // + + char buf[1024]; + memset(buf, '\0', 1024); + int num_bytes = 0; + int total_bytes = 0; + char *write_ptr = buf; + + int type_id = 65; + int field_type = 66; + int field_slice_type = 67; + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 43); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_type_definition(write_ptr, 1024-total_bytes, type_id, GOB_STRUCTTYPE_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_struct_type(write_ptr, 1024-total_bytes, "MyData", type_id); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // field delta for field ([]*fieldType) + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send slice size + num_bytes = gob_start_slice(write_ptr, 1024-total_bytes, 2); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send field type + num_bytes = gob_encode_field_type(write_ptr, 1024-total_bytes, "MyName", GOB_STRING_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send field type + num_bytes = gob_encode_field_type(write_ptr, 1024-total_bytes, "Fields", field_slice_type); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end slice + num_bytes = gob_end_slice(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end struct type + num_bytes = gob_end_struct_type(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end MyType + num_bytes = gob_end_type_definition(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + ///////////////////////////////////////////////////////////////// + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 31); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // []main.FieldData + num_bytes = gob_start_type_definition(write_ptr, 1024-total_bytes, field_slice_type, GOB_SLICETYPE_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_slice_type(write_ptr, 1024-total_bytes, "[]main.FieldData", field_slice_type, field_type); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end []main.FieldData + num_bytes = gob_end_type_definition(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + ///////////////////////////////////////////////////////////////// + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 42); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // FieldData + num_bytes = gob_start_type_definition(write_ptr, 1024-total_bytes, field_type, GOB_STRUCTTYPE_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_struct_type(write_ptr, 1024-total_bytes, "FieldData", field_type); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // field delta for field ([]*fieldType) + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send slice size + num_bytes = gob_start_slice(write_ptr, 1024-total_bytes, 2); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send field type + num_bytes = gob_encode_field_type(write_ptr, 1024-total_bytes, "fFloat", GOB_FLOAT_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // send field type + num_bytes = gob_encode_field_type(write_ptr, 1024-total_bytes, "iInt", GOB_INT_ID); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end slice + num_bytes = gob_end_slice(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end struct type + num_bytes = gob_end_struct_type(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end Field + num_bytes = gob_end_type_definition(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + ///////////////////////////////////////////////////////////////// + + // precomputed message len... + num_bytes = gob_encode_unsigned_int(write_ptr, 1024, 25); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_int(write_ptr, 1024-total_bytes, type_id); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_string(write_ptr, 1024-total_bytes, "sym"); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_start_slice(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // field delta + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_double(write_ptr, 1024-total_bytes, 10.1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // field delta + num_bytes = gob_encode_unsigned_int(write_ptr, 1024-total_bytes, 1); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_encode_int(write_ptr, 1024-total_bytes, 1000); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + num_bytes = gob_end_slice(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end fieldData + num_bytes = gob_end_struct(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + // end MyData + num_bytes = gob_end_struct(write_ptr, 1024-total_bytes); + total_bytes += num_bytes; + write_ptr += num_bytes; + CU_ASSERT(total_bytes < 1024); + + + char result_buf[] = { + 0x2b, // len + 0xff, 0x81, // id negated + 0x03, // offset into wireType (struct type) + 0x01, // offset for commonType + 0x01, // offset for name string + 0x06, // string length 6 + 0x4d, 0x79, 0x44, 0x61, 0x74, 0x61, // "MyData" + 0x01, // offset for _id int + 0xff, 0x82, // id + 0x00, // end of commonType + 0x01, // offset for field []*fieldType + 0x02, // array len = 2 + 0x01, // offset for name string + 0x06, // string len = 6 + 0x4d, 0x79, 0x4e, 0x61, 0x6d, 0x65, // "MyName" + 0x01, // offset for id int + 0x0c, // type string + 0x00, // end of fieldtype + 0x01, // offset for name string + 0x06, // string length + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, // "Fields" + 0x01, // offset for id int + 0xff, 0x86,// id + 0x00, // end of struct fieldType + 0x00, // end of struct structType + 0x00, // end of struct wireType + 0x1f, // len? + 0xff, 0x85,// type id (negated) + 0x02, // offset into wire type (slice type) + 0x01, // offset of common type + 0x01, // offset of name + 0x10, // string length + 0x5b, 0x5d, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x61, //"[]main.FieldData" + 0x01, // offset of id int + 0xff, 0x86,// id + 0x00, // end of commonType + 0x01, // offset of Elem typeId + 0xff, 0x84, // id + 0x00, // end of sliceType + 0x00, // end of wireType + 0x2a, // len + 0xff, 0x83, // id + 0x03, // offset into wireType (struct type) + 0x01, // offset of common type + 0x01, // offset of name string + 0x09, // string length + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x61, // "FieldData" + 0x01, // offset of _id int + 0xff, 0x84, // id + 0x00, // end of commonType + 0x01, // offset of fieldType + 0x02, // array length + 0x01, // offset of name string + 0x06, // string length + 0x66, 0x46, 0x6c, 0x6f, 0x61, 0x74, // "fFloat" + 0x01, // offset of id int + 0x08, // id (float) + 0x00, // end of fieldType + 0x01, // offset of name string + 0x04, // string length + 0x69, 0x49, 0x6e, 0x74, //"iInt" + 0x01, // offset of id int + 0x04, // id (int) + 0x00, // end of fieldType + 0x00, // end of structType + 0x00, // end of wireType + 0x19, // msg len + 0xff, 0x82, // id + 0x01, // offset symbol + 0x03, // string len + 0x73, 0x79, 0x6d, // "sym" + 0x01, // offset of FieldData array + 0x01, // length 1 + 0x01, // offset of fFloat + 0xf8, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x24, 0x40, // fFloat + 0x01, // offset of iInt + 0xfe, 0x07, 0xd0, // iInt + 0x00, // end of struct fieldData + 0x00 // end of struct MyData + }; + + + int i; + for (i = 0; i < total_bytes; i++) { + if (result_buf[i] != buf[i]) { + printf("%2X != %2X at position %d\n", result_buf[i], buf[i], i); + } + } + + CU_ASSERT_EQUAL(146, total_bytes); + CU_ASSERT(memcmp(result_buf, buf, total_bytes) == 0); + +} diff --git a/encode_test.h b/encode_test.h new file mode 100644 index 0000000..9214bb8 --- /dev/null +++ b/encode_test.h @@ -0,0 +1,13 @@ +#ifndef _ENCODE_TEST_H +#define _ENCODE_TEST_H + +void test_gob_encode_unsigned_int(); +void test_flip_unsigned_long_long(); +void test_gob_encode_double(); +void test_gob_encode_int(); +void test_gob_encode_string(); +void test_gob_encode_simple_type(); +void test_gob_encode_more_complex_type(); + +#endif + diff --git a/gob.h b/gob.h new file mode 100644 index 0000000..4cb0c90 --- /dev/null +++ b/gob.h @@ -0,0 +1,22 @@ +#ifndef _GOB_H +#define _GOB_H + +#define GOB_BOOL_ID (1) +#define GOB_INT_ID (2) +#define GOB_UINT_ID (3) +#define GOB_FLOAT_ID (4) +#define GOB_BYTE_SLICE_ID (5) +#define GOB_STRING_ID (6) +#define GOB_COMPLEX_ID (7) +#define GOB_INTERFACE_ID (8) +// gap for reserved ids. +#define GOB_WIRETYPE_ID (16) +#define GOB_ARRAYTYPE_ID (17) +#define GOB_COMMONTYPE_ID (18) +#define GOB_SLICETYPE_ID (19) +#define GOB_STRUCTTYPE_ID (20) +#define GOB_FIELDTYPE_ID (21) +#define GOB_FIELDTYPE_SLICE_ID (22) +#define GOB_MAPTYPE_ID (23) + +#endif diff --git a/test_main.c b/test_main.c new file mode 100644 index 0000000..4c57fc1 --- /dev/null +++ b/test_main.c @@ -0,0 +1,58 @@ +#include "CUnit/Basic.h" +#include "CUnit/Console.h" +#include "CUnit/Automated.h" + +#include "gob.h" +#include "encode.h" +#include "encode_test.h" +#include + +int init_suite() { return 0; } +int clean_suite() { return 0; } + +int main() +{ + CU_pSuite pSuite = NULL; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("encode_suite", init_suite, clean_suite); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if ((NULL == CU_add_test(pSuite, "test_gob_encode_int", test_gob_encode_int)) || + (NULL == CU_add_test(pSuite, "test_gob_encode_unsigned_int", test_gob_encode_unsigned_int)) || + (NULL == CU_add_test(pSuite, "test_gob_encode_double", test_gob_encode_double)) || + (NULL == CU_add_test(pSuite, "test_gob_encode_string", test_gob_encode_string)) || + (NULL == CU_add_test(pSuite, "test_gob_encode_simple_type", test_gob_encode_simple_type)) || + (NULL == CU_add_test(pSuite, "test_gob_encode_more_complex_type", test_gob_encode_more_complex_type)) || + (NULL == CU_add_test(pSuite, "test_flip_unsigned_long_long", test_flip_unsigned_long_long))) + { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + printf("\n"); + CU_basic_show_failures(CU_get_failure_list()); + printf("\n\n"); + + /* Run all tests using the automated interface */ + CU_automated_run_tests(); + CU_list_tests_to_file(); + + /* Run all tests using the console interface */ + //CU_console_run_tests(); + + /* Clean up registry and return */ + CU_cleanup_registry(); + return CU_get_error(); +} -- cgit v1.2.3