summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Miller <graham.miller@gmail.com>2011-01-14 13:40:52 -0500
committerGraham Miller <graham.miller@gmail.com>2011-01-14 13:40:52 -0500
commit8f922d174e08026120363533dbf48349266ee7cb (patch)
tree3439022d0a226e93fec1b2ba284c7767843c256e
downloadlibgob-8f922d174e08026120363533dbf48349266ee7cb.tar.gz
libgob-8f922d174e08026120363533dbf48349266ee7cb.tar.bz2
Added tag 0.1 for changeset 74f74c375a95
-rw-r--r--LICENSE25
-rw-r--r--Makefile44
-rw-r--r--encode.c304
-rw-r--r--encode.h609
-rw-r--r--encode_test.c593
-rw-r--r--encode_test.h13
-rw-r--r--gob.h22
-rw-r--r--test_main.c58
8 files changed, 1668 insertions, 0 deletions
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 <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 <stddef.h>
+
+/**
+ * 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 <stdio.h>
+
+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 <stdio.h>
+
+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();
+}