github.com/ipld/go-ipld-prime@v0.21.0/node/basicnode/map.go (about) 1 package basicnode 2 3 import ( 4 "fmt" 5 6 "github.com/ipld/go-ipld-prime/datamodel" 7 "github.com/ipld/go-ipld-prime/node/mixins" 8 ) 9 10 var ( 11 _ datamodel.Node = &plainMap{} 12 _ datamodel.NodePrototype = Prototype__Map{} 13 _ datamodel.NodeBuilder = &plainMap__Builder{} 14 _ datamodel.NodeAssembler = &plainMap__Assembler{} 15 ) 16 17 // plainMap is a concrete type that provides a map-kind datamodel.Node. 18 // It can contain any kind of value. 19 // plainMap is also embedded in the 'any' struct and usable from there. 20 type plainMap struct { 21 m map[string]datamodel.Node // string key -- even if a runtime schema wrapper is using us for storage, we must have a comparable type here, and string is all we know. 22 t []plainMap__Entry // table for fast iteration, order keeping, and yielding pointers to enable alloc/conv amortization. 23 } 24 25 type plainMap__Entry struct { 26 k plainString // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface. 27 v datamodel.Node // identical to map values. keeping them here simplifies iteration. (in codegen'd maps, this position is also part of amortization, but in this implementation, that's less useful.) 28 // note on alternate implementations: 'v' could also use the 'any' type, and thus amortize value allocations. the memory size trade would be large however, so we don't, here. 29 } 30 31 // -- Node interface methods --> 32 33 func (plainMap) Kind() datamodel.Kind { 34 return datamodel.Kind_Map 35 } 36 func (n *plainMap) LookupByString(key string) (datamodel.Node, error) { 37 v, exists := n.m[key] 38 if !exists { 39 return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)} 40 } 41 return v, nil 42 } 43 func (n *plainMap) LookupByNode(key datamodel.Node) (datamodel.Node, error) { 44 ks, err := key.AsString() 45 if err != nil { 46 return nil, err 47 } 48 return n.LookupByString(ks) 49 } 50 func (plainMap) LookupByIndex(idx int64) (datamodel.Node, error) { 51 return mixins.Map{TypeName: "map"}.LookupByIndex(0) 52 } 53 func (n *plainMap) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) { 54 return n.LookupByString(seg.String()) 55 } 56 func (n *plainMap) MapIterator() datamodel.MapIterator { 57 return &plainMap_MapIterator{n, 0} 58 } 59 func (plainMap) ListIterator() datamodel.ListIterator { 60 return nil 61 } 62 func (n *plainMap) Length() int64 { 63 return int64(len(n.t)) 64 } 65 func (plainMap) IsAbsent() bool { 66 return false 67 } 68 func (plainMap) IsNull() bool { 69 return false 70 } 71 func (plainMap) AsBool() (bool, error) { 72 return mixins.Map{TypeName: "map"}.AsBool() 73 } 74 func (plainMap) AsInt() (int64, error) { 75 return mixins.Map{TypeName: "map"}.AsInt() 76 } 77 func (plainMap) AsFloat() (float64, error) { 78 return mixins.Map{TypeName: "map"}.AsFloat() 79 } 80 func (plainMap) AsString() (string, error) { 81 return mixins.Map{TypeName: "map"}.AsString() 82 } 83 func (plainMap) AsBytes() ([]byte, error) { 84 return mixins.Map{TypeName: "map"}.AsBytes() 85 } 86 func (plainMap) AsLink() (datamodel.Link, error) { 87 return mixins.Map{TypeName: "map"}.AsLink() 88 } 89 func (plainMap) Prototype() datamodel.NodePrototype { 90 return Prototype.Map 91 } 92 93 type plainMap_MapIterator struct { 94 n *plainMap 95 idx int 96 } 97 98 func (itr *plainMap_MapIterator) Next() (k datamodel.Node, v datamodel.Node, _ error) { 99 if itr.Done() { 100 return nil, nil, datamodel.ErrIteratorOverread{} 101 } 102 k = &itr.n.t[itr.idx].k 103 v = itr.n.t[itr.idx].v 104 itr.idx++ 105 return 106 } 107 func (itr *plainMap_MapIterator) Done() bool { 108 return itr.idx >= len(itr.n.t) 109 } 110 111 // -- NodePrototype --> 112 113 type Prototype__Map struct{} 114 115 func (Prototype__Map) NewBuilder() datamodel.NodeBuilder { 116 return &plainMap__Builder{plainMap__Assembler{w: &plainMap{}}} 117 } 118 119 // -- NodeBuilder --> 120 121 type plainMap__Builder struct { 122 plainMap__Assembler 123 } 124 125 func (nb *plainMap__Builder) Build() datamodel.Node { 126 if nb.state != maState_finished { 127 panic("invalid state: assembler must be 'finished' before Build can be called!") 128 } 129 return nb.w 130 } 131 func (nb *plainMap__Builder) Reset() { 132 *nb = plainMap__Builder{} 133 nb.w = &plainMap{} 134 } 135 136 // -- NodeAssembler --> 137 138 type plainMap__Assembler struct { 139 w *plainMap 140 141 ka plainMap__KeyAssembler 142 va plainMap__ValueAssembler 143 144 state maState 145 } 146 type plainMap__KeyAssembler struct { 147 ma *plainMap__Assembler 148 } 149 type plainMap__ValueAssembler struct { 150 ma *plainMap__Assembler 151 } 152 153 // maState is an enum of the state machine for a map assembler. 154 // (this might be something to export reusably, but it's also very much an impl detail that need not be seen, so, dubious.) 155 type maState uint8 156 157 const ( 158 maState_initial maState = iota // also the 'expect key or finish' state 159 maState_midKey // waiting for a 'finished' state in the KeyAssembler. 160 maState_expectValue // 'AssembleValue' is the only valid next step 161 maState_midValue // waiting for a 'finished' state in the ValueAssembler. 162 maState_finished // 'w' will also be nil, but this is a politer statement 163 ) 164 165 func (na *plainMap__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) { 166 if sizeHint < 0 { 167 sizeHint = 0 168 } 169 // Allocate storage space. 170 na.w.t = make([]plainMap__Entry, 0, sizeHint) 171 na.w.m = make(map[string]datamodel.Node, sizeHint) 172 // That's it; return self as the MapAssembler. We already have all the right methods on this structure. 173 return na, nil 174 } 175 func (plainMap__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) { 176 return mixins.MapAssembler{TypeName: "map"}.BeginList(0) 177 } 178 func (plainMap__Assembler) AssignNull() error { 179 return mixins.MapAssembler{TypeName: "map"}.AssignNull() 180 } 181 func (plainMap__Assembler) AssignBool(bool) error { 182 return mixins.MapAssembler{TypeName: "map"}.AssignBool(false) 183 } 184 func (plainMap__Assembler) AssignInt(int64) error { 185 return mixins.MapAssembler{TypeName: "map"}.AssignInt(0) 186 } 187 func (plainMap__Assembler) AssignFloat(float64) error { 188 return mixins.MapAssembler{TypeName: "map"}.AssignFloat(0) 189 } 190 func (plainMap__Assembler) AssignString(string) error { 191 return mixins.MapAssembler{TypeName: "map"}.AssignString("") 192 } 193 func (plainMap__Assembler) AssignBytes([]byte) error { 194 return mixins.MapAssembler{TypeName: "map"}.AssignBytes(nil) 195 } 196 func (plainMap__Assembler) AssignLink(datamodel.Link) error { 197 return mixins.MapAssembler{TypeName: "map"}.AssignLink(nil) 198 } 199 func (na *plainMap__Assembler) AssignNode(v datamodel.Node) error { 200 // Sanity check assembler state. 201 // Update of state to 'finished' comes later; where exactly depends on if shortcuts apply. 202 if na.state != maState_initial { 203 panic("misuse") 204 } 205 // Copy the content. 206 if v2, ok := v.(*plainMap); ok { // if our own type: shortcut. 207 // Copy the structure by value. 208 // This means we'll have pointers into the same internal maps and slices; 209 // this is okay, because the Node type promises it's immutable, and we are going to instantly finish ourselves to also maintain that. 210 // FIXME: the shortcut behaves differently than the long way: it discards any existing progress. Doesn't violate immut, but is odd. 211 *na.w = *v2 212 na.state = maState_finished 213 return nil 214 } 215 // If the above shortcut didn't work, resort to a generic copy. 216 // We call AssignNode for all the child values, giving them a chance to hit shortcuts even if we didn't. 217 if v.Kind() != datamodel.Kind_Map { 218 return datamodel.ErrWrongKind{TypeName: "map", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: v.Kind()} 219 } 220 itr := v.MapIterator() 221 for !itr.Done() { 222 k, v, err := itr.Next() 223 if err != nil { 224 return err 225 } 226 if err := na.AssembleKey().AssignNode(k); err != nil { 227 return err 228 } 229 if err := na.AssembleValue().AssignNode(v); err != nil { 230 return err 231 } 232 } 233 return na.Finish() 234 } 235 func (plainMap__Assembler) Prototype() datamodel.NodePrototype { 236 return Prototype.Map 237 } 238 239 // -- MapAssembler --> 240 241 // AssembleEntry is part of conforming to MapAssembler, which we do on 242 // plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object. 243 func (ma *plainMap__Assembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) { 244 // Sanity check assembler state. 245 // Update of state comes after possible key rejection. 246 if ma.state != maState_initial { 247 panic("misuse") 248 } 249 // Check for dup keys; error if so. 250 _, exists := ma.w.m[k] 251 if exists { 252 return nil, datamodel.ErrRepeatedMapKey{Key: plainString(k)} 253 } 254 ma.state = maState_midValue 255 ma.w.t = append(ma.w.t, plainMap__Entry{k: plainString(k)}) 256 // Make value assembler valid by giving it pointer back to whole 'ma'; yield it. 257 ma.va.ma = ma 258 return &ma.va, nil 259 } 260 261 // AssembleKey is part of conforming to MapAssembler, which we do on 262 // plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object. 263 func (ma *plainMap__Assembler) AssembleKey() datamodel.NodeAssembler { 264 // Sanity check, then update, assembler state. 265 if ma.state != maState_initial { 266 panic("misuse") 267 } 268 ma.state = maState_midKey 269 // Make key assembler valid by giving it pointer back to whole 'ma'; yield it. 270 ma.ka.ma = ma 271 return &ma.ka 272 } 273 274 // AssembleValue is part of conforming to MapAssembler, which we do on 275 // plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object. 276 func (ma *plainMap__Assembler) AssembleValue() datamodel.NodeAssembler { 277 // Sanity check, then update, assembler state. 278 if ma.state != maState_expectValue { 279 panic("misuse") 280 } 281 ma.state = maState_midValue 282 // Make value assembler valid by giving it pointer back to whole 'ma'; yield it. 283 ma.va.ma = ma 284 return &ma.va 285 } 286 287 // Finish is part of conforming to MapAssembler, which we do on 288 // plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object. 289 func (ma *plainMap__Assembler) Finish() error { 290 // Sanity check, then update, assembler state. 291 if ma.state != maState_initial { 292 panic("misuse") 293 } 294 ma.state = maState_finished 295 // validators could run and report errors promptly, if this type had any. 296 return nil 297 } 298 func (plainMap__Assembler) KeyPrototype() datamodel.NodePrototype { 299 return Prototype__String{} 300 } 301 func (plainMap__Assembler) ValuePrototype(_ string) datamodel.NodePrototype { 302 return Prototype.Any 303 } 304 305 // -- MapAssembler.KeyAssembler --> 306 307 func (plainMap__KeyAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) { 308 return mixins.StringAssembler{TypeName: "string"}.BeginMap(0) 309 } 310 func (plainMap__KeyAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) { 311 return mixins.StringAssembler{TypeName: "string"}.BeginList(0) 312 } 313 func (plainMap__KeyAssembler) AssignNull() error { 314 return mixins.StringAssembler{TypeName: "string"}.AssignNull() 315 } 316 func (plainMap__KeyAssembler) AssignBool(bool) error { 317 return mixins.StringAssembler{TypeName: "string"}.AssignBool(false) 318 } 319 func (plainMap__KeyAssembler) AssignInt(int64) error { 320 return mixins.StringAssembler{TypeName: "string"}.AssignInt(0) 321 } 322 func (plainMap__KeyAssembler) AssignFloat(float64) error { 323 return mixins.StringAssembler{TypeName: "string"}.AssignFloat(0) 324 } 325 func (mka *plainMap__KeyAssembler) AssignString(v string) error { 326 // Check for dup keys; error if so. 327 // (And, backtrack state to accepting keys again so we don't get eternally wedged here.) 328 _, exists := mka.ma.w.m[v] 329 if exists { 330 mka.ma.state = maState_initial 331 mka.ma = nil // invalidate self to prevent further incorrect use. 332 return datamodel.ErrRepeatedMapKey{Key: plainString(v)} 333 } 334 // Assign the key into the end of the entry table; 335 // we'll be doing map insertions after we get the value in hand. 336 // (There's no need to delegate to another assembler for the key type, 337 // because we're just at Data Model level here, which only regards plain strings.) 338 mka.ma.w.t = append(mka.ma.w.t, plainMap__Entry{}) 339 mka.ma.w.t[len(mka.ma.w.t)-1].k = plainString(v) 340 // Update parent assembler state: clear to proceed. 341 mka.ma.state = maState_expectValue 342 mka.ma = nil // invalidate self to prevent further incorrect use. 343 return nil 344 } 345 func (plainMap__KeyAssembler) AssignBytes([]byte) error { 346 return mixins.StringAssembler{TypeName: "string"}.AssignBytes(nil) 347 } 348 func (plainMap__KeyAssembler) AssignLink(datamodel.Link) error { 349 return mixins.StringAssembler{TypeName: "string"}.AssignLink(nil) 350 } 351 func (mka *plainMap__KeyAssembler) AssignNode(v datamodel.Node) error { 352 vs, err := v.AsString() 353 if err != nil { 354 return fmt.Errorf("cannot assign non-string node into map key assembler") // FIXME:errors: this doesn't quite fit in ErrWrongKind cleanly; new error type? 355 } 356 return mka.AssignString(vs) 357 } 358 func (plainMap__KeyAssembler) Prototype() datamodel.NodePrototype { 359 return Prototype__String{} 360 } 361 362 // -- MapAssembler.ValueAssembler --> 363 364 func (mva *plainMap__ValueAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) { 365 ma := plainMap__ValueAssemblerMap{} 366 ma.ca.w = &plainMap{} 367 ma.p = mva.ma 368 _, err := ma.ca.BeginMap(sizeHint) 369 return &ma, err 370 } 371 func (mva *plainMap__ValueAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) { 372 la := plainMap__ValueAssemblerList{} 373 la.ca.w = &plainList{} 374 la.p = mva.ma 375 _, err := la.ca.BeginList(sizeHint) 376 return &la, err 377 } 378 func (mva *plainMap__ValueAssembler) AssignNull() error { 379 return mva.AssignNode(datamodel.Null) 380 } 381 func (mva *plainMap__ValueAssembler) AssignBool(v bool) error { 382 vb := plainBool(v) 383 return mva.AssignNode(&vb) 384 } 385 func (mva *plainMap__ValueAssembler) AssignInt(v int64) error { 386 vb := plainInt(v) 387 return mva.AssignNode(&vb) 388 } 389 func (mva *plainMap__ValueAssembler) AssignFloat(v float64) error { 390 vb := plainFloat(v) 391 return mva.AssignNode(&vb) 392 } 393 func (mva *plainMap__ValueAssembler) AssignString(v string) error { 394 vb := plainString(v) 395 return mva.AssignNode(&vb) 396 } 397 func (mva *plainMap__ValueAssembler) AssignBytes(v []byte) error { 398 vb := plainBytes(v) 399 return mva.AssignNode(&vb) 400 } 401 func (mva *plainMap__ValueAssembler) AssignLink(v datamodel.Link) error { 402 vb := plainLink{v} 403 return mva.AssignNode(&vb) 404 } 405 func (mva *plainMap__ValueAssembler) AssignNode(v datamodel.Node) error { 406 l := len(mva.ma.w.t) - 1 407 mva.ma.w.t[l].v = v 408 mva.ma.w.m[string(mva.ma.w.t[l].k)] = v 409 mva.ma.state = maState_initial 410 mva.ma = nil // invalidate self to prevent further incorrect use. 411 return nil 412 } 413 func (plainMap__ValueAssembler) Prototype() datamodel.NodePrototype { 414 return Prototype.Any 415 } 416 417 type plainMap__ValueAssemblerMap struct { 418 ca plainMap__Assembler 419 p *plainMap__Assembler // pointer back to parent, for final insert and state bump 420 } 421 422 // we briefly state only the methods we need to delegate here. 423 // just embedding plainMap__Assembler also behaves correctly, 424 // but causes a lot of unnecessary autogenerated functions in the final binary. 425 426 func (ma *plainMap__ValueAssemblerMap) AssembleEntry(k string) (datamodel.NodeAssembler, error) { 427 return ma.ca.AssembleEntry(k) 428 } 429 func (ma *plainMap__ValueAssemblerMap) AssembleKey() datamodel.NodeAssembler { 430 return ma.ca.AssembleKey() 431 } 432 func (ma *plainMap__ValueAssemblerMap) AssembleValue() datamodel.NodeAssembler { 433 return ma.ca.AssembleValue() 434 } 435 func (plainMap__ValueAssemblerMap) KeyPrototype() datamodel.NodePrototype { 436 return Prototype__String{} 437 } 438 func (plainMap__ValueAssemblerMap) ValuePrototype(_ string) datamodel.NodePrototype { 439 return Prototype.Any 440 } 441 442 func (ma *plainMap__ValueAssemblerMap) Finish() error { 443 if err := ma.ca.Finish(); err != nil { 444 return err 445 } 446 w := ma.ca.w 447 ma.ca.w = nil 448 return ma.p.va.AssignNode(w) 449 } 450 451 type plainMap__ValueAssemblerList struct { 452 ca plainList__Assembler 453 p *plainMap__Assembler // pointer back to parent, for final insert and state bump 454 } 455 456 // we briefly state only the methods we need to delegate here. 457 // just embedding plainList__Assembler also behaves correctly, 458 // but causes a lot of unnecessary autogenerated functions in the final binary. 459 460 func (la *plainMap__ValueAssemblerList) AssembleValue() datamodel.NodeAssembler { 461 return la.ca.AssembleValue() 462 } 463 func (plainMap__ValueAssemblerList) ValuePrototype(_ int64) datamodel.NodePrototype { 464 return Prototype.Any 465 } 466 467 func (la *plainMap__ValueAssemblerList) Finish() error { 468 if err := la.ca.Finish(); err != nil { 469 return err 470 } 471 w := la.ca.w 472 la.ca.w = nil 473 return la.p.va.AssignNode(w) 474 }