22 License MIT. See README.md at the root of this distribution for full copyright
33 and license information.*/
44
5- // Rename keys from their temporary index form to the actual key
6- // looked up in the key dictionary. This is only used when a key
7- // dictionary is read from the end of the input data.
8- const key_re = / ^ _ # _ ( .+ ) $ / ;
9- const PROCESSED = "#(^)#" ;
5+ // Placeholder key construction. Mixed unicode scripts are used to
6+ // minimise risk of collision with real key names.
7+ const SECRET = "ആଈ" ;
8+
9+ // Rename keys from their placeholder form to the actual key looked up
10+ // in the key dictionary.
11+ const key_re = new RegExp ( `^${ SECRET } (.+)$` ) ;
12+
13+ // Key used when a datum has already been processed - removed after
14+ // key remapping. Mixed unicode scripts are used to minimise risk of
15+ // collision with real key names.
16+ const PROCESSED = "ρѓσςεรຂεϑ" ;
1017
1118/**
1219 * Locate uses of key placeholders in data and replace them with
@@ -17,7 +24,7 @@ function remapKeys(data, i2k) {
1724 const processed = [ ] ;
1825
1926 function _remapKeys ( data ) {
20- if ( typeof data === "object" ) {
27+ if ( typeof data === "object" && data !== null ) {
2128 if ( Array . isArray ( data ) ) {
2229 for ( const d of data )
2330 _remapKeys ( d ) ;
@@ -44,26 +51,23 @@ function remapKeys(data, i2k) {
4451}
4552
4653/**
47- * To reduce data volume, it is possible to use a key dictionary (a
48- * list of all known keys used in JS objects). This can save a lot of
49- * space when a lot of similar objects are used.
50- * This mixin can be used in 3 modes:
51- * * known keys, where the caller provides a list of keys they expect
52- * to be there.
53- * * partial keys, where some (but not necessarily all) keys are known
54- * at write time.
55- * * unknown keys, where no keys are known.
56- * The size of the generated binary will vary according to which mode
57- * is used, with known keys being the smallest and fastest, and unknown
58- * keys the largest and slowest.
54+ * To reduce data volume, use a key dictionary (a list of all known
55+ * keys used in JS objects). This can save a lot of space when a lot
56+ * of similar objects are used.
5957 * @mixin KeyDictionaryHandler
6058 */
6159const KeyDictionaryHandler = superclass => class extends superclass {
6260
6361 /**
6462 * The same parameters have to be provided to the tag handlers
6563 * at both ends of the communication.
66- * @param {string[] } options.keys list of keys for the key dictionary
64+ * @param {string[] } options.keys list of known keys for the key.
65+ * Minimum output size will be achieved when this list is complete
66+ * i.e. all possible keys are known in advance.
67+ * @param {function? } options.added optional function called when an
68+ * unknown key is added to the key set. Passed the key and the id it was
69+ * assigned. This can be useful when building a comprehensive key set
70+ * for communication in complex code.
6771 */
6872 constructor ( options ) {
6973 super ( options ) ;
@@ -140,10 +144,12 @@ const KeyDictionaryHandler = superclass => class extends superclass {
140144 return key ;
141145 let id = this . k2i [ key ] ;
142146 if ( typeof id === "undefined" ) {
143- /* istanbul ignore if */
147+ // It might seem tempting to compare the encoding length of the
148+ // id against the raw key length, but it rarely improves the
149+ // data volume enough to make the complexity worthwhile.
144150 this . k2i [ key ] = id = this . i2k . length + this . i2k_added . length ;
145- if ( this . options . debug )
146- this . options . debug ( `\tKDh add ${ key } ${ id } ` ) ;
151+ if ( this . options . added )
152+ this . options . added ( key , id ) ;
147153 this . i2k_added . push ( key ) ;
148154 }
149155 return id ;
@@ -165,7 +171,7 @@ const KeyDictionaryHandler = superclass => class extends superclass {
165171 // the dictionary.
166172 // SMELL: there's a vanishingly small risk that this might
167173 // duplicate a "real" key.
168- return `_#_ ${ id } ` ;
174+ return `${ SECRET } ${ id } ` ;
169175 }
170176} ;
171177
0 commit comments