Replace PB_MANY_FIELDS with PB_FIELD_16BIT and PB_FIELD_32BIT.
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sun, 1 Jul 2012 07:15:37 +0000 (10:15 +0300)
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sun, 1 Jul 2012 07:15:37 +0000 (10:15 +0300)
This allows more precise control over the memory use vs. field size.

docs/reference.rst
generator/nanopb_generator.py
pb.h

index aefc25f..3331c6d 100644 (file)
@@ -10,14 +10,21 @@ Compilation options
 ===================
 The following options can be specified using -D switch given to the C compiler:
 
-============================  ==============================================================================================
+============================  ================================================================================================
 __BIG_ENDIAN__                 Set this if your platform stores integers and floats in big-endian format.
                                Mixed-endian systems (different layout for ints and floats) are currently not supported.
 NANOPB_INTERNALS               Set this to expose the field encoder functions that are hidden since nanopb-0.1.3.
-PB_MAX_REQUIRED_FIELDS         Maximum number of required fields to check for presence. Default value is 64.
-PB_MANY_FIELDS                 Add support for tag numbers > 255 and fields larger than 255 bytes or 255 array entries.
+PB_MAX_REQUIRED_FIELDS         Maximum number of required fields to check for presence. Default value is 64. Increases stack
+                               usage 1 byte per every 8 fields. Compiler warning will tell if you need this.
+PB_FIELD_16BIT                 Add support for tag numbers > 255 and fields larger than 255 bytes or 255 array entries.
+                               Increases code size 3 bytes per each field. Compiler error will tell if you need this.
+PB_FIELD_32BIT                 Add support for tag numbers > 65535 and fields larger than 65535 bytes or 65535 array entries.
                                Increases code size 9 bytes per each field. Compiler error will tell if you need this.
-============================  ==============================================================================================
+============================  ================================================================================================
+
+The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow raising some datatype limits to suit larger messages.
+Their need is recognized automatically by C-preprocessor #if-directives in the generated .pb.h files. The default setting is to use
+the smallest datatypes (least resources used).
 
 pb.h
 ====
@@ -79,7 +86,7 @@ Describes a single structure field with memory position in relation to others. T
 :array_size:    Maximum number of entries in an array, if it is an array type.
 :ptr:           Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE.
 
-The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will give error if the values are too large. The types can be changed to larger ones by defining *PB_MANY_FIELDS*.
+The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will give error if the values are too large. The types can be changed to larger ones by defining *PB_FIELD_16BIT*.
 
 pb_bytes_array_t
 ----------------
index 405feda..9e2a30e 100644 (file)
@@ -252,19 +252,17 @@ class Field:
         
         return result
     
-    def needs_32bit_pb_field_t(self):
-        '''Determine if this field needs 32bit pb_field_t structure to compile properly.
-        Returns True, False or a C-expression for assert.'''
-        if self.tag > 255 or self.max_size > 255:
-            return True
-        
+    def largest_field_value(self):
+        '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
+        Returns numeric value or a C-expression for assert.'''
         if self.ltype == 'PB_LTYPE_SUBMESSAGE':
             if self.htype == 'PB_HTYPE_ARRAY':
-                return 'pb_membersize(%s, %s[0]) > 255' % (self.struct_name, self.name)
+                return 'pb_membersize(%s, %s[0])' % (self.struct_name, self.name)
             else:
-                return 'pb_membersize(%s, %s) > 255' % (self.struct_name, self.name)
-        
-        return False
+                return 'pb_membersize(%s, %s)' % (self.struct_name, self.name)
+
+        return max(self.tag, self.max_size, self.max_count)        
+
 
 class Message:
     def __init__(self, names, desc):
@@ -426,28 +424,41 @@ def generate_header(dependencies, headername, enums, messages):
         yield '         setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
         yield '#endif\n'
     
-    worst = False
+    worst = 0
     worst_field = ''
+    checks = []
     for msg in messages:
         for field in msg.fields:
-            status = field.needs_32bit_pb_field_t()
-            if status == True:
-                worst = True
+            status = field.largest_field_value()
+            if isinstance(status, (str, unicode)):
+                checks.append(status)
+            elif status > worst:
+                worst = status
                 worst_field = str(field.struct_name) + '.' + str(field.name)
-            elif status != False:
-                if worst == False:
-                    worst = status
-                elif worst != True:
-                    worst += ' || ' + status
 
-    if worst != False:
+    if worst > 255 or checks:
         yield '\n/* Check that field information fits in pb_field_t */\n'
-        yield '#ifndef PB_MANY_FIELDS\n'
-        if worst == True:
-            yield '#error Field descriptor for %s is too large. Define PB_MANY_FIELDS to fix this.\n' % worst_field
-        else:
-            yield 'STATIC_ASSERT(!(%s), YOU_MUST_DEFINE_PB_MANY_FIELDS)\n' % worst
-        yield '#endif\n'
+        yield '/* (Largest message has %d fields' % worst
+        if checks: yield ' and submessages have to be checked at compile-time.'
+        yield ') */\n'
+        
+        if worst < 65536:
+            yield '#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)\n'
+            if worst > 255:
+                yield '#error Field descriptor for %s is too large. Define PB_FIELD_16BIT to fix this.\n' % worst_field
+            else:
+                assertion = ' && '.join(str(c) + ' < 256' for c in checks)
+                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT)\n' % assertion
+            yield '#endif\n\n'
+        
+        if worst > 65535 or checks:
+            yield '#if !defined(PB_FIELD_32BIT)\n'
+            if worst > 65535:
+                yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
+            else:
+                assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
+                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT)\n' % assertion
+            yield '#endif\n'
     
     # End of header
     yield '\n#endif\n'
diff --git a/pb.h b/pb.h
index a81e9ef..d030d9e 100644 (file)
--- a/pb.h
+++ b/pb.h
 #define PB_MAX_REQUIRED_FIELDS 64
 #endif
 
+#if PB_MAX_REQUIRED_FIELDS < 64
+#warning You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). \
+         The automatic checks against too low value will not be active.
+#endif
+
 /* List of possible field types. These are used in the autogenerated code.
  * Least-significant 4 bits tell the scalar type
  * Most-significant 4 bits specify repeated/required/packed etc.
@@ -99,21 +104,29 @@ typedef enum {
 
 /* This structure is used in auto-generated constants
  * to specify struct fields.
- * You can change field sizes here if you need structures
+ * You can change field sizes if you need structures
  * larger than 256 bytes or field tags larger than 256.
  * The compiler should complain if your .proto has such
- * structures ("initializer too large for type").
+ * structures. Fix that by defining PB_FIELD_16BIT or
+ * PB_FIELD_32BIT.
  */
 typedef struct _pb_field_t pb_field_t;
 struct _pb_field_t {
 
-#ifndef PB_MANY_FIELDS
+#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
     uint8_t tag;
     pb_type_t type;
     uint8_t data_offset; /* Offset of field data, relative to previous field. */
     int8_t size_offset; /* Offset of array size or has-boolean, relative to data */
     uint8_t data_size; /* Data size in bytes for a single item */
     uint8_t array_size; /* Maximum number of entries in array */
+#elif defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
+    uint16_t tag;
+    pb_type_t type;
+    uint8_t data_offset;
+    int8_t size_offset;
+    uint16_t data_size;
+    uint16_t array_size;
 #else
     uint32_t tag;
     pb_type_t type;