github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/value.go (about) 1 package bas 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "math" 8 "reflect" 9 "strconv" 10 "strings" 11 "time" 12 "unicode/utf8" 13 "unsafe" 14 15 "github.com/coyove/nj/internal" 16 "github.com/coyove/nj/typ" 17 ) 18 19 //go:linkname strhash runtime.strhash 20 func strhash(p unsafe.Pointer, h uintptr) uintptr 21 22 //go:linkname memhash64 runtime.memhash64 23 func memhash64(p unsafe.Pointer, h uintptr) uintptr 24 25 var ( 26 baseMarker = func() []byte { 27 // Ensures baseMarker is at least 256 bytes long and its memory aligns with 256 bytes 28 b := make([]byte, 512) 29 for i := range b { 30 if byte(uintptr(unsafe.Pointer(&b[i]))) == 0 { 31 return b[i:] 32 } 33 } 34 panic("memory") 35 }() 36 baseStart = uintptr(unsafe.Pointer(&baseMarker[0])) 37 baseLength = uintptr(len(baseMarker)) 38 // baseEnd = uintptr(unsafe.Pointer(&baseMarker[0])) + baseLength 39 40 int64Marker = unsafe.Pointer(&baseMarker[int(typ.Number)]) 41 float64Marker = unsafe.Pointer(&baseMarker[int(typ.Number)+8]) 42 trueMarker = unsafe.Pointer(&baseMarker[int(typ.Bool)]) 43 falseMarker = unsafe.Pointer(&baseMarker[int(typ.Bool)+8]) 44 smallStrMarker = unsafe.Pointer(&baseMarker[int(typ.String)]) 45 int64Marker2 = uintptr(int64Marker) * 2 46 47 Nil = Value{} 48 Zero = Int64(0) 49 NullStr = Str("") 50 False = Value{0, falseMarker} 51 True = Value{1, trueMarker} 52 ) 53 54 const ( 55 ValueSize = unsafe.Sizeof(Value{}) 56 57 errNeedNumber = "operator requires number, got %v" 58 errNeedNumbers = "operator requires numbers, got %v and %v" 59 errNeedNumbersOrStrings = "operator requires numbers or strings, got %v and %v" 60 ) 61 62 // Value is the basic data type used by the intepreter, an empty Value naturally represent nil. 63 type Value struct { 64 v uint64 65 p unsafe.Pointer 66 } 67 68 // Type returns the type of value. 69 func (v Value) Type() typ.ValueType { 70 if uintptr(v.p)&0xffffffffffffff00 == baseStart { 71 return typ.ValueType(uintptr(v.p) & 7) 72 } 73 return typ.ValueType(v.v) 74 } 75 76 func (v Value) pType() typ.ValueType { 77 return typ.ValueType(uintptr(v.p) & 7) 78 } 79 80 // IsFalse returns true if value is falsy: nil, false, empty string or 0. 81 func (v Value) IsFalse() bool { return v.v == 0 } 82 83 // IsTrue returns true if value is not falsy. 84 func (v Value) IsTrue() bool { return v.v != 0 } 85 86 // IsInt64 returns true if value is an integer number. 87 func (v Value) IsInt64() bool { return v.p == int64Marker } 88 89 // IsNumber returns true if value is a number. 90 func (v Value) IsNumber() bool { return v.pType() == typ.Number } 91 92 // IsString returns true if value is a string. 93 func (v Value) IsString() bool { return v.Type() == typ.String } 94 95 // IsObject returns true if value is an object. 96 func (v Value) IsObject() bool { return v.Type() == typ.Object } 97 98 // IsArray returns true if value is an array. 99 func (v Value) IsArray() bool { 100 return v.Type() == typ.Native && v.Native().meta.Proto.HasPrototype(&Proto.Array) 101 } 102 103 // IsNil returns true if value is nil. 104 func (v Value) IsNil() bool { return v == Nil } 105 106 // Bool creates a boolean value. 107 func Bool(v bool) Value { 108 if v { 109 return True 110 } 111 return False 112 } 113 114 // Float64 creates a number value. 115 func Float64(f float64) Value { 116 if float64(int64(f)) == f { 117 // if math.Floor(f) == f { 118 return Value{v: uint64(int64(f)), p: int64Marker} 119 } 120 return Value{v: math.Float64bits(f), p: float64Marker} 121 } 122 123 // Int creates a number value. 124 func Int(i int) Value { 125 return Int64(int64(i)) 126 } 127 128 // Int64 creates a number value. 129 func Int64(i int64) Value { 130 return Value{v: uint64(i), p: int64Marker} 131 } 132 133 // Str creates a string value. 134 func Str(s string) Value { 135 if len(s) <= 8 { // payload 8b 136 var x [8]byte 137 switch len(s) { 138 case 1: 139 a := s[0] 140 x = [8]byte{a, a, a, a, a, a, a, a + 1} 141 case 2: 142 a, b := s[0], s[1] 143 x = [8]byte{a, b, a, b, a, b, a, b + 1} 144 case 3: 145 copy(x[:], s) 146 copy(x[3:], s) 147 x[6], x[7] = s[0], s[1]+1 148 case 4, 5, 6, 7: 149 copy(x[:], s) 150 copy(x[len(s):], s) 151 x[7]++ 152 case 8: 153 if s == "\x00\x00\x00\x00\x00\x00\x00\x00" { 154 return Value{v: uint64(typ.String), p: unsafe.Pointer(&s)} 155 } 156 copy(x[:], s) 157 } 158 return Value{ 159 v: binary.BigEndian.Uint64(x[:]), 160 p: unsafe.Pointer(uintptr(smallStrMarker) + uintptr(len(s))*8), 161 } 162 } 163 return Value{v: uint64(typ.String), p: unsafe.Pointer(&s)} 164 } 165 166 // UnsafeStr creates a string value from []byte, its content may change if []byte changed. 167 func UnsafeStr(b []byte) Value { 168 var s string 169 *(*[2]uintptr)(unsafe.Pointer(&s)) = *(*[2]uintptr)(unsafe.Pointer(&b)) 170 return Str(s) 171 } 172 173 // Byte creates a one-byte string value. 174 func Byte(s byte) Value { 175 x := [8]byte{s, s, s, s, s, s, s, s + 1} 176 return Value{v: binary.BigEndian.Uint64(x[:]), p: unsafe.Pointer(uintptr(smallStrMarker) + 8)} 177 } 178 179 // Rune creates a one-rune string value encoded in UTF-8. 180 func Rune(r rune) Value { 181 x := [8]byte{} 182 n := utf8.EncodeRune(x[:], r) 183 switch n { 184 case 1: 185 x[1], x[2], x[3], x[4], x[5], x[6], x[7] = x[0], x[0], x[0], x[0], x[0], x[0], x[0]+1 186 case 2: 187 x[2], x[3], x[4], x[5], x[6], x[7] = x[0], x[1], x[0], x[1], x[0], x[1]+1 188 case 3: 189 x[3], x[4], x[5], x[6], x[7] = x[0], x[1], x[2], x[0], x[1]+1 190 case 4: 191 copy(x[4:], x[:4]) 192 x[7]++ 193 } 194 return Value{v: binary.BigEndian.Uint64(x[:]), p: unsafe.Pointer(uintptr(smallStrMarker) + uintptr(n)*8)} 195 } 196 197 // Bytes creates a bytes array. 198 func Bytes(b []byte) Value { 199 return NewNativeWithMeta(b, &Proto.BytesMeta).ToValue() 200 } 201 202 // Error creates an error, 'e' can be nil, indicating that the returned error has no stacktrace. 203 func Error(e *Env, err interface{}) Value { 204 if err == nil { 205 return Nil 206 } 207 ee := &ExecError{root: err} 208 if e != nil { 209 ee.stacks = e.runtime.Stacktrace(true) 210 } 211 return NewNativeWithMeta(ee, &Proto.ErrorMeta).ToValue() 212 } 213 214 // Array creates an untyped array from values. 215 func Array(v ...Value) Value { 216 return newArray(v...).ToValue() 217 } 218 219 // ValueOf creates a Value from any golang types. 220 func ValueOf(i interface{}) Value { 221 switch v := i.(type) { 222 case nil: 223 return Nil 224 case bool: 225 return Bool(v) 226 case float64: 227 return Float64(v) 228 case int: 229 return Int64(int64(v)) 230 case int8: 231 return Int64(int64(v)) 232 case int16: 233 return Int64(int64(v)) 234 case int32: 235 return Int64(int64(v)) 236 case int64: 237 return Int64(v) 238 case uint: 239 return Int64(int64(uint64(v))) 240 case uint8: 241 return Int64(int64(uint64(v))) 242 case uint16: 243 return Int64(int64(uint64(v))) 244 case uint32: 245 return Int64(int64(uint64(v))) 246 case uint64: 247 return Int64(int64(v)) 248 case uintptr: 249 return Int64(int64(v)) 250 case string: 251 return Str(v) 252 case []byte: 253 return Bytes(v) 254 case *Object: 255 return v.ToValue() 256 case *Map: 257 return newObjectInplace(*v).ToValue() 258 case Map: 259 return newObjectInplace(v).ToValue() 260 case []Value: 261 return Array(v...) 262 case Value: 263 return v 264 case error: 265 return Error(nil, v) 266 case reflect.Value: 267 return ValueOf(v.Interface()) 268 case func(*Env): 269 return Func(internal.UnnamedFunc(), v) 270 } 271 272 if rv := reflect.ValueOf(i); rv.Kind() == reflect.Func { 273 rt := rv.Type() 274 nf := func(env *Env) { 275 rtNumIn := rt.NumIn() 276 ins := make([]reflect.Value, 0, rtNumIn) 277 if !rt.IsVariadic() { 278 if env.Size() != rtNumIn { 279 internal.Panic("native function expects %d arguments, got %d", rtNumIn, env.Size()) 280 } 281 for i := 0; i < rtNumIn; i++ { 282 ins = append(ins, env.Get(i).ToType(rt.In(i))) 283 } 284 } else { 285 if env.Size() < rtNumIn-1 { 286 internal.Panic("native variadic function expects at least %d arguments, got %d", rtNumIn-1, env.Size()) 287 } 288 for i := 0; i < rtNumIn-1; i++ { 289 ins = append(ins, env.Get(i).ToType(rt.In(i))) 290 } 291 for i := rtNumIn - 1; i < env.Size(); i++ { 292 ins = append(ins, env.Get(i).ToType(rt.In(rtNumIn-1).Elem())) 293 } 294 } 295 if outs := rv.Call(ins); len(outs) == 0 { 296 env.A = Nil 297 } else if len(outs) == 1 { 298 env.A = ValueOf(outs[0].Interface()) 299 } else { 300 env.A = NewNative(outs).ToValue() 301 } 302 } 303 return Func("<"+rt.String()+">", nf) 304 } 305 return NewNative(i).ToValue() 306 } 307 308 func (v Value) isSmallString() bool { 309 return uintptr(v.p) >= uintptr(smallStrMarker) && uintptr(v.p) <= uintptr(smallStrMarker)+8*8 310 } 311 312 // Str returns value as a string, Type() should be checked beforehand. 313 func (v Value) Str() string { 314 if v.isSmallString() { 315 sz := (uintptr(v.p) - uintptr(smallStrMarker)) / 8 316 if sz == 0 { 317 return "" 318 } 319 buf := make([]byte, 8) 320 binary.BigEndian.PutUint64(buf, v.v) 321 buf = buf[:sz] 322 return *(*string)(unsafe.Pointer(&buf)) 323 } 324 return *(*string)(v.p) 325 } 326 327 // Int returns value as an int. 328 func (v Value) Int() int { return int(v.Int64()) } 329 330 // Int64 returns value as an int64 (floats will be truncated to integers), Type() should be checked beforehand. 331 func (v Value) Int64() int64 { 332 if v.p == int64Marker { 333 return int64(v.v) 334 } 335 return int64(math.Float64frombits(v.v)) 336 } 337 338 // Float64 returns value as a float (integers will be promoted to floats), Type() should be checked beforehand. 339 func (v Value) Float64() float64 { 340 if v.p == int64Marker { 341 return float64(int64(v.v)) 342 } 343 return math.Float64frombits(v.v) 344 } 345 346 // Duration returns the number value as a time.Duration, 347 // it assumes the number represents the Unix timestamp in seconds. 348 func (v Value) Duration() time.Duration { 349 return time.Duration(v.Float64()*1e6) * 1e3 350 } 351 352 // Bool returns value as a boolean, Type() should be checked beforehand. 353 func (v Value) Bool() bool { return v.p == trueMarker } 354 355 // Object returns value as an Object, Type() should be checked beforehand. 356 func (v Value) Object() *Object { return (*Object)(v.p) } 357 358 // Native returns value as a Native, Type() should be checked beforehand. 359 func (v Value) Native() *Native { return (*Native)(v.p) } 360 361 // Interface returns value as an interface{} 362 func (v Value) Interface() interface{} { 363 switch v.Type() { 364 case typ.Bool: 365 return v.Bool() 366 case typ.Number: 367 if v.IsInt64() { 368 return v.Int64() 369 } 370 return v.Float64() 371 case typ.String: 372 return v.Str() 373 case typ.Object: 374 return v.Object() 375 case typ.Native: 376 return v.Native().Unwrap() 377 } 378 return nil 379 } 380 381 func (v Value) unsafeAddr() uintptr { return uintptr(v.p) } 382 383 func (v Value) UnsafeInt64() int64 { return int64(v.v) } 384 385 func (v Value) UnsafeFloat64() float64 { return math.Float64frombits(v.v) } 386 387 // Equal returns true if two values are equal. 388 func (v Value) Equal(r Value) bool { 389 if v == r { 390 return true 391 } 392 return v.v == uint64(typ.String) && v.v == r.v && *(*string)(v.p) == *(*string)(r.p) 393 } 394 395 // HashCode returns the hash of value. 396 func (v Value) HashCode() uint32 { 397 if typ.ValueType(v.v) == typ.String { 398 return uint32(strhash(v.p, 0)) 399 } 400 401 x := uint64(uintptr(v.p)) 402 xp := uintptr(unsafe.Pointer(&x)) 403 h := memhash64(unsafe.Pointer(xp^0), uintptr(v.v)) 404 return uint32(h) 405 406 // x := v.v ^ uint64(uintptr(v.p)) 407 // h := uint32(x) ^ uint32(x>>32) 408 // h ^= h >> 16 409 // h *= 0x85ebca6b 410 // h ^= h >> 13 411 // // h *= 0xc2b2ae35 412 // // h ^= h >> 16 413 // return h 414 } 415 416 func (v Value) String() string { 417 p := &bytes.Buffer{} 418 v.Stringify(p, typ.MarshalToString) 419 return p.String() 420 } 421 422 func (v Value) JSONString() string { 423 p := &bytes.Buffer{} 424 v.Stringify(p, typ.MarshalToJSON) 425 return strings.TrimSpace(p.String()) 426 } 427 428 func (v Value) MarshalJSON() ([]byte, error) { 429 p := &bytes.Buffer{} 430 v.Stringify(p, typ.MarshalToJSON) 431 return bytes.TrimSpace(p.Bytes()), nil 432 } 433 434 func (v Value) Stringify(p io.Writer, j typ.MarshalType) { 435 switch v.Type() { 436 case typ.Bool: 437 internal.WriteString(p, strconv.FormatBool(v.Bool())) 438 case typ.Number: 439 if v.IsInt64() { 440 internal.WriteString(p, strconv.FormatInt(v.Int64(), 10)) 441 } else { 442 internal.WriteString(p, strconv.FormatFloat(v.Float64(), 'f', -1, 64)) 443 } 444 case typ.String: 445 internal.WriteString(p, internal.IfQuote(j == typ.MarshalToJSON, v.Str())) 446 case typ.Object: 447 if j == typ.MarshalToJSON || j == typ.MarshalToString { 448 v.Object().rawPrint(p, j) 449 } else { 450 internal.WriteString(p, v.Object().Name()) 451 } 452 case typ.Native: 453 if j == typ.MarshalToJSON || j == typ.MarshalToString { 454 v.Native().Marshal(p, j) 455 } else { 456 internal.WriteString(p, "@"+v.Native().meta.Name) 457 } 458 default: 459 internal.WriteString(p, internal.IfStr(j == typ.MarshalToJSON, "null", "nil")) 460 } 461 }