github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/object.go (about) 1 package bas 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math" 8 "strconv" 9 "strings" 10 "unsafe" 11 12 "github.com/coyove/nj/internal" 13 "github.com/coyove/nj/typ" 14 ) 15 16 type Object struct { 17 parent *Object 18 fun *funcbody 19 local Map 20 this Value 21 } 22 23 func NewObject(size int) *Object { 24 obj := newObjectInplace(Map{}) 25 obj.local.Init(size) 26 return obj 27 } 28 29 func newObjectInplace(m Map) *Object { 30 obj := &Object{} 31 obj.local = m 32 obj.this = obj.ToValue() 33 obj.parent = &Proto.Object 34 obj.fun = objDefaultFun 35 return obj 36 } 37 38 func NewNamedObject(name string, size int) *Object { 39 return NewObject(size).setName(name) 40 } 41 42 func (m *Object) setName(name string) *Object { 43 m.fun = &funcbody{name: name, native: func(*Env) {}} 44 return m 45 } 46 47 func (m *Object) Prototype() *Object { 48 if m == nil { 49 return nil 50 } 51 return m.parent 52 } 53 54 func (m *Object) SetPrototype(m2 *Object) *Object { 55 m.parent = m2 56 return m 57 } 58 59 func (m *Object) HasPrototype(proto *Object) bool { 60 for ; m != nil; m = m.parent { 61 if m == proto { 62 return true 63 } 64 } 65 return false 66 } 67 68 // Cap returns the capacity of the object. 69 func (m *Object) Cap() int { 70 if m == nil { 71 return 0 72 } 73 return m.local.Cap() 74 } 75 76 // Len returns the count of local properties in the object. 77 func (m *Object) Len() int { 78 if m == nil { 79 return 0 80 } 81 return m.local.Len() 82 } 83 84 // Clear clears all local properties in the object. 85 func (m *Object) Clear() { 86 m.local.Clear() 87 } 88 89 // SetProp sets property by string 'name', which is short for Set(Str(name), v). 90 func (m *Object) SetProp(name string, v Value) *Object { 91 m.Set(Str(name), v) 92 return m 93 } 94 95 // AddMethod binds function 'fun' to property 'name' in the object, making 'fun' a method of the object. 96 // This differs from 'Set(name, Func(name, fun))' because the latter one, 97 // as not being a method, can't use 'this' argument when called. 98 func (m *Object) AddMethod(name string, fun func(*Env)) *Object { 99 f := Func(m.Name()+"."+name, fun) 100 f.Object().fun.method = true 101 m.Set(Str(name), f) 102 return m 103 } 104 105 // Find retrieves the property by 'name', returns false as the second argument if not found. 106 func (m *Object) Find(name Value) (v Value, exists bool) { 107 if m == nil { 108 return Nil, false 109 } 110 return m.find(name, true) 111 } 112 113 // Get retrieves the property by 'name'. 114 func (m *Object) Get(name Value) (v Value) { 115 if m == nil { 116 return Nil 117 } 118 v, _ = m.find(name, true) 119 return v 120 } 121 122 // GetDefault retrieves the property by 'name', returns 'defaultValue' if not found. 123 func (m *Object) GetDefault(name, defaultValue Value) (v Value) { 124 if m == nil { 125 return defaultValue 126 } 127 if v, ok := m.find(name, true); ok { 128 return v 129 } 130 return defaultValue 131 } 132 133 func (m *Object) find(k Value, setReceiver bool) (v Value, ok bool) { 134 v, ok = m.local.Get(k) 135 if !ok && m.parent != nil { 136 v, ok = m.parent.find(k, false) 137 } 138 if setReceiver && v.IsObject() { 139 if obj := v.Object(); obj.fun.method { 140 f := obj.Copy() 141 f.this = m.ToValue() 142 v = f.ToValue() 143 } 144 } 145 return 146 } 147 148 // Contains returns true if object contains property 'name', inherited properties will also be checked. 149 func (m *Object) Contains(name Value) bool { 150 if m == nil { 151 return false 152 } 153 found := m.local.Contains(name) 154 if !found { 155 found = m.parent.Contains(name) 156 } 157 return found 158 } 159 160 // HasOwnProperty returns true if 'name' is a local property in the object. 161 func (m *Object) HasOwnProperty(name Value) bool { 162 if m == nil { 163 return false 164 } 165 return m.local.Contains(name) 166 } 167 168 // Set sets a local property in the object. Inherited property with the same name will be shadowed. 169 func (m *Object) Set(name, v Value) (prev Value) { 170 return m.local.Set(name, v) 171 } 172 173 // Delete deletes a local property from the object. Inherited properties are omitted and never deleted. 174 func (m *Object) Delete(name Value) (prev Value) { 175 return m.local.Delete(name) 176 } 177 178 // Foreach iterates all local properties in the object, refer to 'Map.Foreach' for more helps. 179 func (m *Object) Foreach(f func(Value, *Value) bool) { 180 if m == nil { 181 return 182 } 183 m.local.Foreach(f) 184 } 185 186 func (m *Object) internalNext(kv Value) Value { 187 if kv == Nil { 188 kv = Array(Nil, Nil) 189 } 190 nk, nv := m.local.FindNext(kv.Native().Get(0)) 191 if nk == Nil { 192 return Nil 193 } 194 kv.Native().Set(0, nk) 195 kv.Native().Set(1, nv) 196 return kv 197 } 198 199 func (m *Object) String() string { 200 return m.local.String() 201 } 202 203 func (m *Object) rawPrint(p io.Writer, j typ.MarshalType) { 204 if m == nil { 205 internal.WriteString(p, internal.IfStr(j == typ.MarshalToJSON, "null", "nil")) 206 return 207 } 208 if j != typ.MarshalToJSON { 209 if m.fun != objDefaultFun && m.fun != nil { 210 internal.WriteString(p, m.funcSig()) 211 } 212 } 213 m.local.rawPrint(p, j) 214 } 215 216 func (m *Object) ToValue() Value { 217 if m == nil { 218 return Nil 219 } 220 return Value{v: uint64(typ.Object), p: unsafe.Pointer(m)} 221 } 222 223 func (m *Object) Name() string { 224 if m == &Proto.Object { 225 return objDefaultFun.name 226 } 227 if m != nil && m.fun != nil { 228 if m.fun.name == objDefaultFun.name { 229 return m.parent.Name() 230 } 231 return m.fun.name 232 } 233 return objDefaultFun.name 234 } 235 236 func (m *Object) Copy() *Object { 237 if m == nil { 238 return NewObject(0) 239 } 240 m2 := *m 241 if m.local.count > 0 { 242 m2.local = m.local.Copy() 243 } 244 if m2.fun == nil { 245 // Some empty objects don't have proper structures, 246 // normally they are declared directly instead of using NewObject. 247 m2.fun = objDefaultFun 248 m2.parent = &Proto.Object 249 } 250 return &m2 251 } 252 253 func (m *Object) Merge(src *Object) *Object { 254 if src != nil && src.Len() > 0 { 255 m.local.Merge(&src.local) 256 } 257 return m 258 } 259 260 func (m *Object) ToMap() Map { 261 if m == nil { 262 return Map{} 263 } 264 return m.local 265 } 266 267 type Map struct { 268 noresize bool 269 count uint32 270 items []hashItem 271 } 272 273 // hashItem represents a slot in the map. 274 type hashItem struct { 275 key, val Value 276 dist int32 277 hash16 uint16 278 pDeleted bool 279 } 280 281 func newMap(size int) *Map { 282 obj := &Map{} 283 obj.Init(size) 284 return obj 285 } 286 287 // Init pre-allocates enough memory for 'count' key and clears all old data. 288 func (m *Map) Init(count int) *Map { 289 if count > 0 { 290 m.count = 0 291 m.items = make([]hashItem, count*2) 292 } 293 return m 294 } 295 296 // Cap returns the capacity of the map in terms of key-value pairs, one pair is (ValueSize * 2 + 8) bytes. 297 func (m Map) Cap() int { 298 return len(m.items) 299 } 300 301 // Len returns the count of keys in the map. 302 func (m Map) Len() int { 303 return int(m.count) 304 } 305 306 // Clear clears all keys in the map, where already allocated memory will be reused. 307 func (m *Map) Clear() { 308 for i := range m.items { 309 m.items[i] = hashItem{} 310 } 311 m.count = 0 312 } 313 314 // Get retrieves the value by 'k', returns false as the second argument if not found. 315 func (m Map) Get(k Value) (v Value, exists bool) { 316 if idx := m.findValue(k); idx >= 0 { 317 return m.items[idx].val, true 318 } 319 return Nil, false 320 } 321 322 func (m *Map) findValue(k Value) int { 323 num := len(m.items) 324 if num <= 0 || k == Nil { 325 return -1 326 } 327 idx := int(k.HashCode() % uint32(num)) 328 idxStart := idx 329 330 for { 331 e := &m.items[idx] 332 if e.key == Nil { 333 if !e.pDeleted { 334 return -1 335 } 336 } 337 338 if e.key.Equal(k) { 339 return idx 340 } 341 342 idx = (idx + 1) % num 343 if idx == idxStart { 344 return -1 345 } 346 } 347 } 348 349 // Contains returns true if the map contains 'k'. 350 func (m Map) Contains(k Value) bool { 351 return m.findValue(k) >= 0 352 } 353 354 // Set upserts a key-value pair in the map. Nil key is not allowed. 355 func (m *Map) Set(k, v Value) (prev Value) { 356 if k == Nil { 357 internal.Panic("key can't be nil") 358 } 359 if len(m.items) <= 0 { 360 m.items = make([]hashItem, 8) 361 } 362 if int(m.count) >= len(m.items)*3/4 { 363 m.resizeHash(len(m.items) * 2) 364 } 365 return m.setHash(hashItem{key: k, val: v}) 366 } 367 368 // Delete deletes a key from the map, returns deleted value if existed 369 func (m *Map) Delete(k Value) (prev Value) { 370 idx := m.findValue(k) 371 if idx < 0 { 372 return Nil 373 } 374 current := &m.items[idx] 375 current.pDeleted = true 376 current.key = Nil 377 m.count-- 378 return current.val 379 } 380 381 func (m *Map) setHash(incoming hashItem) (prev Value) { 382 num := len(m.items) 383 idx := int(incoming.key.HashCode() % uint32(num)) 384 385 for idxStart := idx; ; { 386 e := &m.items[idx] 387 if e.pDeleted { 388 // Shift the following keys forward 389 this := idx 390 for startIdx := this; ; { 391 next := (this + 1) % num 392 if m.items[next].dist > 0 { 393 m.items[this] = m.items[next] 394 m.items[this].dist-- 395 this = next 396 if this != startIdx { 397 continue 398 } 399 } 400 break 401 } 402 m.items[this] = hashItem{} 403 continue 404 } 405 406 if e.key == Nil { 407 m.items[idx] = incoming 408 m.count++ 409 return Nil 410 } 411 412 if e.key.Equal(incoming.key) { 413 prev = e.val 414 e.val, e.dist, e.pDeleted = incoming.val, incoming.dist, false 415 return prev 416 } 417 418 // Swap if the incoming item is further from its best idx. 419 if e.dist < incoming.dist { 420 incoming, m.items[idx] = m.items[idx], incoming 421 } 422 423 incoming.dist++ // One step further away from best idx. 424 idx = (idx + 1) % num 425 426 if idx == idxStart { 427 if internal.IsDebug() { 428 fmt.Println(m.items) 429 } 430 panic("object space not enough") 431 } 432 } 433 } 434 435 // Foreach iterates all keys in the map, for each of them, 'f(key, &value)' will be 436 // called. Values are passed by pointers and it is legal to manipulate them directly in 'f'. 437 // Deletions are allowed during Foreach(), but the iteration may be incomplete therefore. 438 func (m Map) Foreach(f func(Value, *Value) bool) { 439 for i := 0; i < len(m.items); i++ { 440 ip := &m.items[i] 441 if ip.key != Nil && !ip.pDeleted { 442 if !f(ip.key, &ip.val) { 443 return 444 } 445 } 446 } 447 } 448 449 func (m *Map) nextHashPair(start int) (Value, Value) { 450 for i := start; i < len(m.items); i++ { 451 if p := &m.items[i]; p.key != Nil && !p.pDeleted { 452 return p.key, p.val 453 } 454 } 455 return Nil, Nil 456 } 457 458 // FindNext finds the next key after 'k', returns nil if not found. 459 // The output is stable between map changes (e.g. Delete). 460 func (m Map) FindNext(k Value) (Value, Value) { 461 if k == Nil { 462 return m.nextHashPair(0) 463 } 464 idx := m.findValue(k) 465 if idx < 0 { 466 return Nil, Nil 467 } 468 return m.nextHashPair(idx + 1) 469 } 470 471 func (m Map) String() string { 472 p := &bytes.Buffer{} 473 m.rawPrint(p, typ.MarshalToString) 474 return p.String() 475 } 476 477 func (m Map) rawPrint(p io.Writer, j typ.MarshalType) { 478 needComma := false 479 internal.WriteString(p, "{") 480 m.Foreach(func(k Value, v *Value) bool { 481 internal.WriteString(p, internal.IfStr(needComma, ",", "")) 482 k.Stringify(p, j.NoRec()) 483 internal.WriteString(p, internal.IfStr(j == typ.MarshalToJSON, ":", "=")) 484 v.Stringify(p, j.NoRec()) 485 needComma = true 486 return true 487 }) 488 internal.WriteString(p, "}") 489 } 490 491 func (m Map) Copy() Map { 492 m.items = append([]hashItem{}, m.items...) 493 return m 494 } 495 496 func (m *Map) Merge(src *Map) *Map { 497 if src.Len() > 0 { 498 m.resizeHash((m.Len() + src.Len()) * 2) 499 src.Foreach(func(k Value, v *Value) bool { m.Set(k, *v); return true }) 500 } 501 return m 502 } 503 504 func (m *Map) resizeHash(newSize int) { 505 if m.noresize { 506 return 507 } 508 if newSize <= len(m.items) { 509 return 510 } 511 tmp := Map{items: make([]hashItem, newSize)} 512 for _, e := range m.items { 513 if e.key != Nil { 514 e.dist = 0 515 tmp.setHash(e) 516 } 517 } 518 m.items = tmp.items 519 } 520 521 func (m Map) density() float64 { 522 num := len(m.items) 523 if num <= 0 || m.count <= 0 { 524 return math.NaN() 525 } 526 527 var maxRun int 528 for i := 0; i < num; { 529 if m.items[i].key == Nil { 530 i++ 531 continue 532 } 533 run := 1 534 for i++; i < num; i++ { 535 if m.items[i].key != Nil { 536 run++ 537 } else { 538 break 539 } 540 } 541 if run > maxRun { 542 maxRun = run 543 } 544 } 545 return float64(maxRun) / (float64(num) / float64(m.count)) 546 } 547 548 func (m Map) DebugString() string { 549 p := bytes.Buffer{} 550 for idx, i := range m.items { 551 p.WriteString(strconv.Itoa(idx) + ":") 552 if i.pDeleted { 553 p.WriteString("\t" + strings.Repeat(".", int(i.dist)) + "deleted\n") 554 } else if i.key == Nil { 555 p.WriteString("\t-\n") 556 } else { 557 at := i.key.HashCode() % uint32(len(m.items)) 558 if i.dist > 0 { 559 p.WriteString(fmt.Sprintf("^%d", at)) 560 } 561 p.WriteString("\t" + strings.Repeat(".", int(i.dist)) + fmt.Sprintf("%v\n", i.key)) 562 } 563 } 564 return p.String() 565 } 566 567 func (m Map) ToObject() *Object { 568 return newObjectInplace(m) 569 }