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  }