David Rose 21 лет назад
Родитель
Сommit
876dd16b39
1 измененных файлов с 248 добавлено и 0 удалено
  1. 248 0
      direct/src/doc/dcPacker.txt

+ 248 - 0
direct/src/doc/dcPacker.txt

@@ -0,0 +1,248 @@
+This document introduces the basic use of the DCPacker class, which is
+available to C++ and Python programs for high-level packing and
+unpacking of messages into bytestreams for shipping over the network,
+especially via Panda's DistributedObject system.  See also the
+comments in direct/src/dcparser/dcPacker.h and related source files.
+
+
+OVERVIEW
+
+The DCPacker has four modes of operation: pack (sequential write),
+unpack (sequential read), unpack (random read), and repack (random
+write).
+
+To enter one of these four modes, call begin_pack(), begin_unpack(),
+or begin_repack().  (begin_unpack() is used for both kinds of unpack
+modes.)  Once you have called begin, you can call a series of
+pack_this() or unpack_that() methods, and then you finish up by
+calling end_pack(), end_unpack(), or end_repack().
+
+The return value of the end method will be true to indicate that no
+errors have occurred during the packing/unpacking process, or false if
+something went wrong (in which case you should probably disregard the
+output).
+
+In general, when packing or unpacking a series of values, you call
+pack_int(), pack_uint(), pack_double(), or pack_string() (or the
+corresponding unpack methods) according to what kind of data type you
+have for each value; it will be coerced into the appropriate data size
+as indicated by the DC file and written to the output buffer.
+
+To pack an array or an embedded class, or any element which itself is
+made up of sub-elements, you must bracket the packs for the
+sub-elements between calls to push() and pop().  This also applies to
+the individual elements of a DCField; so to pack all the elements of a
+field, you would call push(), followed by the appropriate pack() for
+each element, then pop().
+
+
+PACK MODE (sequential write)
+
+Pack mode is used to build up a network message from scratch.  Call
+begin_pack() and pass it the pointer to a DCField object.  You must
+immediately call push() to indicate that you will be packing the
+individual elements of the field, then make a series of pack calls,
+one for each element on the field in order, followed by a call to
+pop(), and finally end_pack().
+
+You must pack all of the elements of the field, from beginning to
+end--it is an error to leave out any elements, including the elements
+on the end.
+
+If end_pack() returns false, there was an error (see ADDITIONAL NOTES,
+below).  Otherwise, you may call get_data() to get a pointer to the
+packed data record, and get_length() to get the number of bytes in the
+record.
+
+  DCField *field = dclass->get_field_by_name("setChat");
+
+  DCPacker packer;
+  packer.begin_pack(field);
+  packer.push();
+  packer.pack_string(chatString);
+  packer.pack_int(0);
+  packer.pop();
+  if (!packer.end_pack()) {
+    cerr << "error occurred while packing.\n";
+    return;
+  }
+
+  memcpy(result, packer.get_data(), packer.get_length());
+
+
+
+UNPACK MODE (sequential read)
+
+You can also unpack all the elements of a field, from beginning to
+end.  This is very similar to pack mode, above.  Start with a call to
+begin_unpack() and pass in the existing data record for the field, and
+the pointer to the DCField itself.  Then call push(), followed by the
+appropriate number and type of unpack calls, followed by pop() and
+end_unpack().
+
+As above, you must unpack all fields; it is an error not to unpack the
+fields on the end.  However, it is not an error if there are
+additional bytes in the data buffer; the assumption is the data buffer
+may be part of a larger buffer.  After end_unpack(), you can call
+get_num_unpacked_bytes() to determine how many bytes of the buffer
+were consumed.
+
+  DCField *field = dclass->get_field_by_name("setChat");
+
+  DCPacker packer;
+  packer.begin_unpack(source_buffer, source_size, field);
+  packer.push();
+  string chat = packer.unpack_string();
+  int chatFlags = packer.unpack_int();
+  packer.pop();
+  if (!packer.end_unpack()) {
+    cerr << "error occurred while unpacking.\n";
+    return;
+  }
+
+
+UNPACK MODE (random read)
+
+You can also unpack just the particular elements that you care about
+by name, in no particular order.  To do this, call seek() for each
+element you wish to unpack, specifying the name of the element.  You
+can only do this for elements that have been given names in the DC
+file.
+
+In this case, it is not necessary to bracket the outer unpack calls
+with push() and pop() (since you are not walking through all the
+elements of the field).  However, you still need to use push() and
+pop() to unpack the nested elements of an array that you seek to.
+
+  DCField *field = dclass->get_field_by_name("setChat");
+
+  DCPacker packer;
+  packer.begin_unpack(source_buffer, source_size, field);
+  packer.seek("chat");
+  string chat = packer.unpack_string();
+  if (!packer.end_unpack()) {
+    cerr << "error occurred while unpacking.\n";
+    return;
+  }
+
+
+REPACK MODE (random write)
+
+Repack mode allows you to modify some elements of a previously-packed
+field, without disturbing the elements you don't specify.
+begin_repack() takes the same parameters as begin_unpack(), then call
+seek() for each field you want to modify followed by the appropriate
+pack call.
+
+After end_repack() returns true, you can retrieve the newly-repacked
+field with get_data() and get_length(), just as in pack mode.
+
+  DCField *field = dclass->get_field_by_name("setChat");
+
+  DCPacker packer;
+  packer.begin_repack(source_buffer, source_size, field);
+  packer.seek("chat");
+  packer.pack_string(chatString);
+  if (!packer.end_repack()) {
+    cerr << "error occurred while repacking.\n";
+    return;
+  }
+
+  memcpy(result, packer.get_data(), packer.get_length());
+
+
+
+ADDITIONAL NOTES
+
+It is acceptable to call pack_int() for a uint type element and
+vice-versa; the data type will be range-checked and converted to the
+appropriate signedness.  In general, all of the numeric types are
+interchangeable--just call the appropriate one according to the data
+type you already have; don't worry about matching to the data type
+defined in the DC file.  However, if you are trying to write a general
+algorithm and you need a hint, you can call get_pack_type() to return
+a suggested type for the next pack call; this will return one of
+PT_int, PT_uint, PT_double, PT_string, etc.
+
+The same is true when unpacking: unpack_int() or unpack_uint() may be
+used interchangeably on signed or unsigned data (but if you call
+unpack_uint() and the data in the record happens to be negative, you
+will trigger a pack error).  As above, get_pack_type() may be called
+to return the suggested type for the next unpack call.
+
+
+If end_pack() or end_repack() returns false, there are two possible
+causes.  (1) You tried to pack some value that exceeded the range
+specified in the DC file (or the limits of the datatype).  In this
+case, had_range_error() will return true.  (2) There was some other,
+more serious error while packing the data, such as a mismatched type
+(e.g. pack_string() where a uint16 was expected), or you did not pack
+the right number of elements.  In this case, had_pack_error() will
+return true.  It might be the case that both error flags are
+triggered.
+
+If end_unpack() returns false, there are two similar causes.  (1)
+There was an invalid value in the record that exceeded the limits
+specified in the DC file.  This will be indicated by
+had_range_error().  (2) Some mismatched data type (unpack_string() for
+a uint16) or the wrong number of elements.  This is indicated by
+had_pack_error().  Note that specifying a too-small return value
+(e.g. unpack_uint() to retrieve a signed value, or unpack_int() to
+retrieve a float64 or int64 value greater than 2^32) is considered a
+pack error, not a range error.
+
+
+You may call pack_literal_value() for any element for which you want
+to supply a pre-packed data value (for instance, a default value
+returned by DCAtomicField::get_element_default()).  This will be
+accepted without further validation.  Similarly,
+unpack_literal_value() will return a string corresponding to the
+pre-packed value of the current element.  Both of these work for
+composite elements as well as for single-component elements (that is,
+you may call unpack_literal_value() instead of calling push()
+.. unpack .. pop() to retrieve an entire pre-packed array in one
+string).
+
+Python programmers may be especially interested in pack_object() and
+unpack_object().  pack_object() will accept any Python object and call
+the appropriate pack function for it.  Python tuple or list will
+implicitly call push(), followed by pack_object() for all the elements
+in the list, followed by pop(), so pack_object() can pack deeply
+nested structures with a single call, and with no need to call push()
+and pop() explicitly.  Conversely, unpack_object() will unpack a
+deeply nested structure and return an appropriate Python tuple or list
+or other object.  You may also consider DCField::pack_args() and
+DCField::unpack_args(), which automatically invokes the DCPacker for
+you.
+
+You may also find parse_and_pack() and unpack_and_format() useful for
+presenting data to (and accepting data from) a human user.
+parse_and_pack() accepts a string formatted in the DC file syntax
+(that is, with the same syntax accepted for a DC file default value),
+and packs that value for the current element.  It may be a single
+value or a deeply nested value, with brackets and braces embedded as
+appropriate.  Similarly, unpack_and_format() will unpack a single
+value or a deeply nested value into the same formatted string.  As
+with pack_object() and unpack_object(), these methods are also
+implemented on the DCField class for convenience, as parse_string()
+and format_data().
+
+
+RANGE VALIDATION
+
+The DCPacker automatically verifies that all data passing through its
+fundamental pack or unpack methods fits within the ranges (if any)
+specified in the DC file for each data type.  Violating a range
+restriction triggers a range error, which is indicated by a false
+return value from end_pack() / end_unpack() / end_repack() and by a
+true return value from had_range_error().
+
+If you just want to verify that a message contains legal values
+without otherwise inspecting the values, you can use unpack_validate()
+for this purpose.  Since unpack_validate() will work on deeply nested
+structures, you can just call it once in lieu of the entire push()
+.. pack .. pop() loop.  Furthermore, as in unpack_object() and
+unpack_and_format(), above, there is a convenience function for this
+on DCField; just call DCField::validate_ranges() to ensure that the
+data in the record for the given field fits within its specified
+limits.