github.com/philpearl/plenc@v0.0.15/plenccodec/json.go (about) 1 package plenccodec 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "unsafe" 7 8 "github.com/philpearl/plenc/plenccore" 9 ) 10 11 // One map we handle is one that can deal with JSON map[string]any. In 12 // this case the value is either nil, string, integer, float64, bool, array (of 13 // these types) or object (another map[string]interface). We would need to 14 // encode the value type as the standard wire types of proto don't encode enough 15 // info. Encode as a list of key value pairs with a value type field. There's 16 // some unfortunate overlap between the types we need and wire types, but it's 17 // probably better to make consistent use of the proto encoding?? Why is this 18 // better than just serialising with JSON and dumping the bytes? It's probably a 19 // bit denser has faster to parse. 20 21 // JSONMapCodec is for serialising JSON maps encoded in Go as 22 // map[string]any. To use this codec you must register it for use with 23 // map[string]any or a named map[string]any type 24 type JSONMapCodec struct{} 25 26 // JSONArrayCodec is for serialising JSON arrays encoded as []any 27 type JSONArrayCodec struct{} 28 29 type jsonType uint 30 31 const ( 32 jsonTypeNil jsonType = iota 33 jsonTypeString 34 jsonTypeInt 35 jsonTypeFloat 36 jsonTypeBool 37 jsonTypeArray 38 jsonTypeObject 39 jsonTypeNumber 40 ) 41 42 func (JSONMapCodec) Omit(ptr unsafe.Pointer) bool { 43 return ptr == nil 44 } 45 46 func (c JSONMapCodec) size(ptr unsafe.Pointer) (size int) { 47 // this is just a map pointer here! 48 var m map[string]any 49 *(*unsafe.Pointer)((unsafe.Pointer)(&m)) = ptr 50 51 // We'll use the WTSlice wire type, so first is the number of items 52 size = plenccore.SizeVarUint(uint64(len(m))) 53 for k, v := range m { 54 // With WTSlice each item is preceeded by its length 55 itemSize := c.sizeKV(k, v) 56 size += plenccore.SizeVarUint(uint64(itemSize)) + itemSize 57 } 58 return size 59 } 60 61 func (c JSONMapCodec) sizeKV(k string, v any) (size int) { 62 size += plenccore.SizeTag(StringCodec{}.WireType(), 1) 63 size += plenccore.SizeVarUint(uint64(len(k))) 64 size += len(k) 65 return size + sizeJSONValue(v) 66 } 67 68 func (c JSONMapCodec) append(data []byte, ptr unsafe.Pointer) []byte { 69 // this is just a map pointer here! 70 var m map[string]any 71 *(*unsafe.Pointer)((unsafe.Pointer)(&m)) = ptr 72 73 // First the number of items 74 data = plenccore.AppendVarUint(data, uint64(len(m))) 75 76 // Next each item preceeded by its length 77 for k, v := range m { 78 s := c.sizeKV(k, v) 79 data = plenccore.AppendVarUint(data, uint64(s)) 80 data = c.appendKV(data, k, v) 81 } 82 83 return data 84 } 85 86 var keyTag = plenccore.AppendTag(nil, StringCodec{}.WireType(), 1) 87 88 func (c JSONMapCodec) appendKV(data []byte, k string, v any) []byte { 89 data = StringCodec{}.Append(data, unsafe.Pointer(&k), keyTag) 90 return appendJSONValue(data, v) 91 } 92 93 func (c JSONMapCodec) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 94 count, n := plenccore.ReadVarUint(data) 95 if n == 0 { 96 return 0, nil 97 } 98 offset := n 99 100 m := *(*map[string]any)(ptr) 101 if m == nil { 102 m = make(map[string]any, count) 103 *(*map[string]any)(ptr) = m 104 } 105 106 for ; count > 0; count-- { 107 l, n := plenccore.ReadVarUint(data[offset:]) 108 if n < 0 { 109 return 0, fmt.Errorf("bad length in map") 110 } 111 offset += n 112 var key string 113 var val any 114 115 n, err := readJSONKV(data[offset:offset+int(l)], &key, &val) 116 if err != nil { 117 return 0, err 118 } 119 offset += n 120 m[key] = val 121 } 122 123 return offset, nil 124 } 125 126 func (c JSONMapCodec) New() unsafe.Pointer { 127 m := make(map[string]any) 128 return unsafe.Pointer(&m) 129 } 130 131 func (c JSONMapCodec) WireType() plenccore.WireType { return plenccore.WTSlice } 132 133 func (c JSONMapCodec) Descriptor() Descriptor { 134 // This needs to be a special descriptor! 135 return Descriptor{Type: FieldTypeJSONObject} 136 } 137 138 func (c JSONMapCodec) Size(ptr unsafe.Pointer, tag []byte) int { 139 return c.size(ptr) + len(tag) 140 } 141 142 func (c JSONMapCodec) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 143 data = append(data, tag...) 144 return c.append(data, ptr) 145 } 146 147 func (c JSONArrayCodec) Omit(ptr unsafe.Pointer) bool { 148 return (ptr == nil) || (len(*(*[]any)(ptr)) == 0) 149 } 150 151 func (c JSONArrayCodec) size(ptr unsafe.Pointer) (size int) { 152 a := *(*[]any)(ptr) 153 size = plenccore.SizeVarUint(uint64(len(a))) 154 // Each entry is encoded preceeded by its length 155 for _, val := range a { 156 itemSize := sizeJSONValue(val) 157 size += plenccore.SizeVarUint(uint64(itemSize)) + itemSize 158 } 159 return size 160 } 161 162 func (c JSONArrayCodec) append(data []byte, ptr unsafe.Pointer) []byte { 163 a := *(*[]any)(ptr) 164 data = plenccore.AppendVarUint(data, uint64(len(a))) 165 // Each entry is encoded preceeded by its length 166 for _, val := range a { 167 itemSize := sizeJSONValue(val) 168 data = plenccore.AppendVarUint(data, uint64(itemSize)) 169 data = appendJSONValue(data, val) 170 } 171 return data 172 } 173 174 func (c JSONArrayCodec) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireType) (n int, err error) { 175 count, n := plenccore.ReadVarUint(data) 176 offset := n 177 178 a := *(*[]any)(ptr) 179 if a == nil { 180 a = make([]any, count) 181 *(*[]any)(ptr) = a 182 } 183 184 for i := range a { 185 l, n := plenccore.ReadVarUint(data[offset:]) 186 if n < 0 { 187 return 0, fmt.Errorf("bad length in map") 188 } 189 offset += n 190 191 n, err := readJSONKV(data[offset:offset+int(l)], nil, &a[i]) 192 if err != nil { 193 return 0, err 194 } 195 offset += n 196 } 197 198 return offset, nil 199 } 200 201 func (c JSONArrayCodec) New() unsafe.Pointer { 202 return unsafe.Pointer(&[]any{}) 203 } 204 205 func (c JSONArrayCodec) WireType() plenccore.WireType { return plenccore.WTSlice } 206 207 func (c JSONArrayCodec) Descriptor() Descriptor { 208 // This needs to be a special descriptor! 209 return Descriptor{Type: FieldTypeJSONArray} 210 } 211 212 func (c JSONArrayCodec) Size(ptr unsafe.Pointer, tag []byte) int { 213 return c.size(ptr) + len(tag) 214 } 215 216 func (c JSONArrayCodec) Append(data []byte, ptr unsafe.Pointer, tag []byte) []byte { 217 data = append(data, tag...) 218 return c.append(data, ptr) 219 } 220 221 func sizeJSONValue(v any) (size int) { 222 size += plenccore.SizeTag(plenccore.WTVarInt, 2) 223 switch v := v.(type) { 224 case nil: 225 size += plenccore.SizeVarUint(uint64(jsonTypeNil)) 226 case string: 227 size += plenccore.SizeVarUint(uint64(jsonTypeString)) 228 229 size += StringCodec{}.Size(unsafe.Pointer(&v), valueWTLTag) 230 case int: 231 size += plenccore.SizeVarUint(uint64(jsonTypeInt)) 232 size += IntCodec[int]{}.Size(unsafe.Pointer(&v), valueWTVITag) 233 case float64: 234 size += plenccore.SizeVarUint(uint64(jsonTypeFloat)) 235 size += Float64Codec{}.Size(unsafe.Pointer(&v), valueWT64Tag) 236 case bool: 237 size += plenccore.SizeVarUint(uint64(jsonTypeBool)) 238 size += BoolCodec{}.Size(unsafe.Pointer(&v), valueWTVITag) 239 case []any: 240 size += plenccore.SizeVarUint(uint64(jsonTypeArray)) 241 size += JSONArrayCodec{}.Size(unsafe.Pointer(&v), valueWTSliceTag) 242 case map[string]any: 243 size += plenccore.SizeVarUint(uint64(jsonTypeObject)) 244 size += JSONMapCodec{}.Size(unsafe.Pointer(unpackEFace(v).data), valueWTSliceTag) 245 case json.Number: 246 // Save this as a string 247 size += plenccore.SizeVarUint(uint64(jsonTypeNumber)) 248 size += StringCodec{}.Size(unsafe.Pointer(&v), valueWTLTag) 249 default: 250 panic(fmt.Sprintf("unexpected json type %T", v)) 251 } 252 253 return size 254 } 255 256 var ( 257 valueWTLTag = plenccore.AppendTag(nil, plenccore.WTLength, 3) 258 valueWTVITag = plenccore.AppendTag(nil, plenccore.WTVarInt, 3) 259 valueWT64Tag = plenccore.AppendTag(nil, plenccore.WT64, 3) 260 valueWTSliceTag = plenccore.AppendTag(nil, plenccore.WTSlice, 3) 261 ) 262 263 func appendJSONValue(data []byte, v any) []byte { 264 data = plenccore.AppendTag(data, plenccore.WTVarInt, 2) 265 switch v := v.(type) { 266 case nil: 267 data = plenccore.AppendVarUint(data, uint64(jsonTypeNil)) 268 case string: 269 data = plenccore.AppendVarUint(data, uint64(jsonTypeString)) 270 data = StringCodec{}.Append(data, unsafe.Pointer(&v), valueWTLTag) 271 case int: 272 data = plenccore.AppendVarUint(data, uint64(jsonTypeInt)) 273 data = IntCodec[int]{}.Append(data, unsafe.Pointer(&v), valueWTVITag) 274 case float64: 275 data = plenccore.AppendVarUint(data, uint64(jsonTypeFloat)) 276 data = Float64Codec{}.Append(data, unsafe.Pointer(&v), valueWT64Tag) 277 case bool: 278 data = plenccore.AppendVarUint(data, uint64(jsonTypeBool)) 279 data = BoolCodec{}.Append(data, unsafe.Pointer(&v), valueWTVITag) 280 case []any: 281 data = plenccore.AppendVarUint(data, uint64(jsonTypeArray)) 282 data = JSONArrayCodec{}.Append(data, unsafe.Pointer(&v), valueWTSliceTag) 283 case map[string]any: 284 data = plenccore.AppendVarUint(data, uint64(jsonTypeObject)) 285 data = JSONMapCodec{}.Append(data, unsafe.Pointer(unpackEFace(v).data), valueWTSliceTag) 286 case json.Number: 287 // Save this as a string 288 data = plenccore.AppendVarUint(data, uint64(jsonTypeNumber)) 289 data = StringCodec{}.Append(data, unsafe.Pointer(&v), valueWTLTag) 290 default: 291 panic(fmt.Sprintf("unexpected json type %T", v)) 292 } 293 294 return data 295 } 296 297 func readJSONKV(data []byte, key *string, val *any) (n int, err error) { 298 var ( 299 jType jsonType 300 offset int 301 ) 302 303 for offset < len(data) { 304 wt, index, n := plenccore.ReadTag(data[offset:]) 305 offset += n 306 switch index { 307 case 1: 308 // When using this for reading arrays we simply don't see this index 309 l, n := plenccore.ReadVarUint(data[offset:]) 310 if n < 0 { 311 return 0, fmt.Errorf("bad length on string field") 312 } 313 offset += n 314 315 n, err := StringCodec{}.Read(data[offset:offset+int(l)], unsafe.Pointer(key), wt) 316 if err != nil { 317 return 0, err 318 } 319 offset += n 320 case 2: 321 v, n := plenccore.ReadVarUint(data[offset:]) 322 if n < 0 { 323 return 0, fmt.Errorf("invalid map type field") 324 } 325 jType = jsonType(v) 326 offset += n 327 case 3: 328 switch jType { 329 case jsonTypeString: 330 l, n := plenccore.ReadVarUint(data[offset:]) 331 if n < 0 { 332 return 0, fmt.Errorf("bad length on string field") 333 } 334 offset += n 335 var v string 336 n, err := StringCodec{}.Read(data[offset:offset+int(l)], unsafe.Pointer(&v), wt) 337 if err != nil { 338 return 0, err 339 } 340 *val = v 341 offset += n 342 343 case jsonTypeInt: 344 var v int 345 n, err := IntCodec[int]{}.Read(data[offset:], unsafe.Pointer(&v), wt) 346 if err != nil { 347 return 0, err 348 } 349 offset += n 350 *val = v 351 352 case jsonTypeFloat: 353 var v float64 354 n, err := Float64Codec{}.Read(data[offset:], unsafe.Pointer(&v), wt) 355 if err != nil { 356 return 0, err 357 } 358 offset += n 359 *val = v 360 361 case jsonTypeBool: 362 var v bool 363 n, err := BoolCodec{}.Read(data[offset:], unsafe.Pointer(&v), wt) 364 if err != nil { 365 return 0, err 366 } 367 offset += n 368 *val = v 369 370 case jsonTypeArray: 371 var v []any 372 n, err := JSONArrayCodec{}.Read(data[offset:], unsafe.Pointer(&v), wt) 373 if err != nil { 374 return 0, err 375 } 376 offset += n 377 *val = v 378 379 case jsonTypeObject: 380 var v map[string]any 381 n, err := JSONMapCodec{}.Read(data[offset:], unsafe.Pointer(&v), wt) 382 if err != nil { 383 return 0, err 384 } 385 offset += n 386 *val = v 387 388 case jsonTypeNumber: 389 l, n := plenccore.ReadVarUint(data[offset:]) 390 if n < 0 { 391 return 0, fmt.Errorf("bad length on JSON number field") 392 } 393 offset += n 394 var v json.Number 395 n, err := StringCodec{}.Read(data[offset:offset+int(l)], unsafe.Pointer(&v), wt) 396 if err != nil { 397 return 0, err 398 } 399 *val = v 400 offset += n 401 402 default: 403 return 0, fmt.Errorf("unexpected json type %d", jType) 404 } 405 } 406 } 407 408 return offset, nil 409 }