github.com/git-amp/amp-sdk-go@v0.7.5/amp/support.core.go (about) 1 package amp 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "strings" 7 "time" 8 "unsafe" 9 10 "github.com/git-amp/amp-sdk-go/stdlib/bufs" 11 ) 12 13 // URI form of a glyph typically followed by a media (mime) type. 14 const GlyphURIPrefix = "amp:glyph/" 15 16 // Describes an asset to be an image stream but not specify format / codec 17 var GenericImageType = "image/*" 18 19 // UTC16 is a signed UTC timestamp, storing the elapsed 1/65536 second ticks since Jan 1, 1970 UTC. 20 // 21 // Shifting this value to the right 16 bits will yield standard Unix time. 22 // This means there are 47 bits dedicated for seconds, implying a limit 4.4 million years. 23 type UTC16 int64 24 25 // TID identifies a specific planet, node, or transaction. 26 // 27 // Unless otherwise specified a TID in the wild should always be considered read-only. 28 type TID []byte 29 30 // TIDBuf is embedded UTC16 value followed by a 24 byte hash. 31 type TIDBuf [TIDBinaryLen]byte 32 33 // Byte size of a TID, a hash with a leading embedded big endian binary time index. 34 const TIDBinaryLen = int(Const_TIDBinaryLen) 35 36 // ASCII string length of a CellTID encoded into its base32 form. 37 const TIDStringLen = int(Const_TIDStringLen) 38 39 // nilTID is a zeroed TID that denotes a void/nil/zero value of a TID 40 var nilTID = TID{} 41 42 const ( 43 SI_DistantFuture = UTC16(0x7FFFFFFFFFFFFFFF) 44 ) 45 46 // Converts a time.Time to a UTC16. 47 func ConvertToUTC16(t time.Time) UTC16 { 48 time16 := t.Unix() << 16 49 frac := uint16((2199 * (uint32(t.Nanosecond()) >> 10)) >> 15) 50 return UTC16(time16 | int64(frac)) 51 } 52 53 // Similar to time.Now() but returns a UTC16. 54 func Now() UTC16 { 55 return ConvertToUTC16(time.Now()) 56 } 57 58 // Converts milliseconds to UTC16. 59 func ConvertMsToUTC(ms int64) UTC16 { 60 return UTC16((ms << 16) / 1000) 61 } 62 63 // Converts UTC16 to a time.Time. 64 func (t UTC16) ToTime() time.Time { 65 return time.Unix(int64(t>>16), int64(t&0xFFFF)*15259) 66 } 67 68 // Converts UTC16 to milliseconds. 69 func (t UTC16) ToMs() int64 { 70 return (int64(t>>8) * 1000) >> 8 71 } 72 73 // TID is a convenience function that returns the TID contained within this TxID. 74 func (tid *TIDBuf) TID() TID { 75 return tid[:] 76 } 77 78 // Base32 returns this TID in Base32 form. 79 func (tid *TIDBuf) Base32() string { 80 return bufs.Base32Encoding.EncodeToString(tid[:]) 81 } 82 83 // IsNil returns true if this TID length is 0 or is equal to NilTID 84 func (tid TID) IsNil() bool { 85 if len(tid) == 0 { 86 return true 87 } 88 89 if bytes.Equal(tid, nilTID[:]) { 90 return true 91 } 92 93 return false 94 } 95 96 // Clone returns a duplicate of this TID 97 func (tid TID) Clone() TID { 98 dupe := make([]byte, len(tid)) 99 copy(dupe, tid) 100 return dupe 101 } 102 103 // Buf is a convenience function that make a new TxID from a TID byte slice. 104 func (tid TID) Buf() (buf TIDBuf) { 105 copy(buf[:], tid) 106 return buf 107 } 108 109 // Base32 returns this TID in Base32 form. 110 func (tid TID) Base32() string { 111 return bufs.Base32Encoding.EncodeToString(tid) 112 } 113 114 // Appends the base 32 ASCII encoding of this TID to the given buffer 115 func (tid TID) AppendAsBase32(in []byte) []byte { 116 encLen := bufs.Base32Encoding.EncodedLen(len(tid)) 117 needed := len(in) + encLen 118 buf := in 119 if needed > cap(buf) { 120 buf = make([]byte, (needed+0x100)&^0xFF) 121 buf = append(buf[:0], in...) 122 } 123 buf = buf[:needed] 124 bufs.Base32Encoding.Encode(buf[len(in):needed], tid) 125 return buf 126 } 127 128 // SuffixStr returns the last few digits of this TID in string form (for easy reading, logs, etc) 129 func (tid TID) SuffixStr() string { 130 const summaryStrLen = 5 131 132 R := len(tid) 133 L := R - summaryStrLen 134 if L < 0 { 135 L = 0 136 } 137 return bufs.Base32Encoding.EncodeToString(tid[L:R]) 138 } 139 140 // SetTimeAndHash writes the given timestamp and the right-most part of inSig into this TID. 141 // 142 // See comments for TIDBinaryLen 143 func (tid TID) SetTimeAndHash(time UTC16, hash []byte) { 144 tid.SetUTC(time) 145 tid.SetHash(hash) 146 } 147 148 // SetHash sets the sig/hash portion of this ID 149 func (tid TID) SetHash(hash []byte) { 150 const TIDHashSz = int(Const_TIDBinaryLen - 8) 151 pos := len(hash) - TIDHashSz 152 if pos >= 0 { 153 copy(tid[TIDHashSz:], hash[pos:]) 154 } else { 155 for i := 8; i < int(Const_TIDBinaryLen); i++ { 156 tid[i] = hash[i] 157 } 158 } 159 } 160 161 // SetUTC16 writes the given UTC16 into this TID 162 func (tid TID) SetUTC(t UTC16) { 163 tid[0] = byte(t >> 56) 164 tid[1] = byte(t >> 48) 165 tid[2] = byte(t >> 40) 166 tid[3] = byte(t >> 32) 167 tid[4] = byte(t >> 24) 168 tid[5] = byte(t >> 16) 169 tid[6] = byte(t >> 8) 170 tid[7] = byte(t) 171 } 172 173 // ExtractUTC16 returns the unix timestamp embedded in this TID (a unix timestamp in 1<<16 seconds UTC) 174 func (tid TID) ExtractUTC() UTC16 { 175 t := int64(tid[0]) 176 t = (t << 8) | int64(tid[1]) 177 t = (t << 8) | int64(tid[2]) 178 t = (t << 8) | int64(tid[3]) 179 t = (t << 8) | int64(tid[4]) 180 t = (t << 8) | int64(tid[5]) 181 t = (t << 8) | int64(tid[6]) 182 t = (t << 8) | int64(tid[7]) 183 184 return UTC16(t) 185 } 186 187 // ExtractTime returns the unix timestamp embedded in this TID (a unix timestamp in seconds UTC) 188 func (tid TID) ExtractTime() int64 { 189 t := int64(tid[0]) 190 t = (t << 8) | int64(tid[1]) 191 t = (t << 8) | int64(tid[2]) 192 t = (t << 8) | int64(tid[3]) 193 t = (t << 8) | int64(tid[4]) 194 t = (t << 8) | int64(tid[5]) 195 196 return t 197 } 198 199 // SelectEarlier looks in inTime a chooses whichever is earlier. 200 // 201 // If t is later than the time embedded in this TID, then this function has no effect and returns false. 202 // 203 // If t is earlier, then this TID is initialized to t (and the rest zeroed out) and returns true. 204 func (tid TID) SelectEarlier(t UTC16) bool { 205 206 TIDt := tid.ExtractUTC() 207 208 // Timestamp of 0 is reserved and should only reflect an invalid/uninitialized TID. 209 if t < 0 { 210 t = 0 211 } 212 213 if t < TIDt || t == 0 { 214 tid.SetUTC(t) 215 for i := 8; i < len(tid); i++ { 216 tid[i] = 0 217 } 218 return true 219 } 220 221 return false 222 } 223 224 // CopyNext copies the given TID and increments it by 1, typically useful for seeking the next entry after a given one. 225 func (tid TID) CopyNext(inTID TID) { 226 copy(tid, inTID) 227 for j := len(tid) - 1; j > 0; j-- { 228 tid[j]++ 229 if tid[j] > 0 { 230 break 231 } 232 } 233 } 234 235 // CellID is unique Cell identifier that globally identifies a cell. 236 type CellID [16]byte 237 238 // Forms a CellID from uint64s. 239 func CellIDFromU64(x0, x1 uint64) (id CellID) { 240 id.AssignFromU64(x0, x1) 241 return id 242 } 243 244 func (id *CellID) IsNil() bool { 245 return *(*int64)(unsafe.Pointer(id)) == 0 && *(*int64)(unsafe.Pointer(&id[8])) == 0 246 } 247 248 func (id *CellID) AssignFromU64(x0, x1 uint64) { 249 binary.BigEndian.PutUint64(id[0:8], x0) 250 binary.BigEndian.PutUint64(id[8:16], x1) 251 } 252 253 func (id *CellID) ExportAsU64() (x0, x1 uint64) { 254 x0 = binary.BigEndian.Uint64(id[0:8]) 255 x1 = binary.BigEndian.Uint64(id[8:16]) 256 return 257 } 258 259 func (id *CellID) String() string { 260 return bufs.Base32Encoding.EncodeToString(id[:]) 261 } 262 263 /* 264 // Issues a CellID using the given random number generator to generate the UID hash portion. 265 func IssueCellID(rng *rand.Rand) (id CellID) { 266 return CellIDFromU64(uint64(ConvertToUTC16(time.Now())), rng.Uint64()) 267 } 268 269 // TID identifies a Tx (or Cell) by secure hash ID. 270 type TxID struct { 271 UTC16 UTC16 272 Hash1 uint64 273 Hash2 uint64 274 Hash3 uint64 275 } 276 277 // Base32 returns this TID in Base32 form. 278 func (tid *TxID) Base32() string { 279 var bin [TIDBinaryLen]byte 280 binStr := tid.AppendAsBinary(bin[:0]) 281 return bufs.Base32Encoding.EncodeToString(binStr) 282 } 283 284 // Appends the base 32 ASCII encoding of this TID to the given buffer 285 func (tid *TxID) AppendAsBase32(io []byte) []byte { 286 L := len(io) 287 288 needed := L + TIDStringLen 289 dst := io 290 if needed > cap(dst) { 291 dst = make([]byte, (needed+0x100)&^0xFF) 292 dst = append(dst[:0], io...) 293 } 294 dst = dst[:needed] 295 296 var bin [TIDBinaryLen]byte 297 binStr := tid.AppendAsBinary(bin[:0]) 298 299 bufs.Base32Encoding.Encode(dst[L:needed], binStr) 300 return dst 301 } 302 303 // Appends the base 32 ASCII encoding of this TID to the given buffer 304 func (tid *TxID) AppendAsBinary(io []byte) []byte { 305 L := len(io) 306 needed := L + TIDBinaryLen 307 dst := io 308 if needed > cap(dst) { 309 dst = make([]byte, needed) 310 dst = append(dst[:0], io...) 311 } 312 dst = dst[:needed] 313 314 binary.BigEndian.PutUint64(dst[L+0:L+8], uint64(tid.UTC16)) 315 binary.BigEndian.PutUint64(dst[L+8:L+16], tid.Hash1) 316 binary.BigEndian.PutUint64(dst[L+16:L+24], tid.Hash2) 317 binary.BigEndian.PutUint64(dst[L+24:L+32], tid.Hash3) 318 return dst 319 } 320 */ 321 322 func (id ConstSymbol) Ord() uint32 { 323 return uint32(id) 324 } 325 326 // Analyses an AttrSpec's SeriesSpec and returns the index class it uses. 327 func GetSeriesIndexType(seriesSpec string) SeriesIndexType { 328 switch { 329 case strings.HasSuffix(seriesSpec, ".Name"): 330 return SeriesIndexType_Name 331 default: 332 return SeriesIndexType_Literal 333 } 334 } 335 336 func (params *PinReqParams) URLPath() []string { 337 if params.URL == nil { 338 return nil 339 } 340 path := params.URL.Path 341 if path != "" && path[0] == '/' { 342 path = path[1:] 343 } 344 return strings.Split(path, "/") 345 } 346 347 func (params *PinReqParams) Params() *PinReqParams { 348 return params 349 } 350 351 /* 352 353 // ReadCell loads a cell with the given URI having the inferred schema (built from its fields using reflection). 354 // The URI is scoped into the user's home planet and AppID. 355 func ReadCell(ctx AppContext, subKey string, schema *AttrSchema, dstStruct any) error { 356 357 dst := reflect.Indirect(reflect.ValueOf(dstStruct)) 358 switch dst.Kind() { 359 case reflect.Pointer: 360 dst = dst.Elem() 361 case reflect.Struct: 362 default: 363 return ErrCode_ExportErr.Errorf("expected struct, got %v", dst.Kind()) 364 } 365 366 var keyBuf [128]byte 367 cellKey := append(append(keyBuf[:0], []byte(ctx.StateScope())...), []byte(subKey)...) 368 369 msgs := make([]*Msg, 0, len(schema.Attrs)) 370 err := ctx.User().HomePlanet().ReadCell(cellKey, schema, func(msg *Msg) { 371 switch msg.Op { 372 case MsgOp_PushAttr: 373 msgs = append(msgs, msg) 374 } 375 }) 376 if err != nil { 377 return err 378 } 379 380 numFields := dst.NumField() 381 valType := dst.Type() 382 383 for fi := 0; fi < numFields; fi++ { 384 field := valType.Field(fi) 385 for _, ai := range schema.Attrs { 386 if ai.TypedName == field.Name { 387 for _, msg := range msgs { 388 if msg.AttrID == ai.AttrID { 389 msg.LoadVal(dst.Field(fi).Addr().Interface()) 390 goto nextField 391 } 392 } 393 } 394 } 395 nextField: 396 } 397 return err 398 } 399 400 // WriteCell is the write analog of ReadCell. 401 func WriteCell(ctx AppContext, subKey string, schema *AttrSchema, srcStruct any) error { 402 403 src := reflect.Indirect(reflect.ValueOf(srcStruct)) 404 switch src.Kind() { 405 case reflect.Pointer: 406 src = src.Elem() 407 case reflect.Struct: 408 default: 409 return ErrCode_ExportErr.Errorf("expected struct, got %v", src.Kind()) 410 } 411 412 { 413 tx := NewMsgBatch() 414 msg := tx.AddMsg() 415 msg.Op = MsgOp_UpsertCell 416 msg.ValType = ValType_SchemaID.Ord() 417 msg.ValInt = int64(schema.SchemaID) 418 msg.ValBuf = append(append(msg.ValBuf[:0], []byte(ctx.StateScope())...), []byte(subKey)...) 419 420 numFields := src.NumField() 421 valType := src.Type() 422 423 for _, attr := range schema.Attrs { 424 msg := tx.AddMsg() 425 msg.Op = MsgOp_PushAttr 426 msg.AttrID = attr.AttrID 427 for i := 0; i < numFields; i++ { 428 if valType.Field(i).Name == attr.TypedName { 429 msg.setVal(src.Field(i).Interface()) 430 break 431 } 432 } 433 if msg.ValType == ValType_nil.Ord() { 434 panic("missing field") 435 } 436 } 437 438 msg = tx.AddMsg() 439 msg.Op = MsgOp_Commit 440 441 if err := ctx.User().HomePlanet().PushTx(tx); err != nil { 442 return err 443 } 444 } 445 446 return nil 447 } 448 449 450 func (req *CellReq) GetKwArg(argKey string) (string, bool) { 451 for _, arg := range req.Args { 452 if arg.Key == argKey { 453 if arg.Val != "" { 454 return arg.Val, true 455 } 456 return string(arg.ValBuf), true 457 } 458 } 459 return "", false 460 } 461 462 func (req *CellReq) GetChildSchema(modelURI string) *AttrSchema { 463 for _, schema := range req.ChildSchemas { 464 if schema.CellDataModel == modelURI { 465 return schema 466 } 467 } 468 return nil 469 } 470 471 func (req *CellReq) PushBeginPin(target CellID) { 472 m := NewMsg() 473 m.CellID = target.U64() 474 m.Op = MsgOp_PinCell 475 req.PushUpdate(m) 476 } 477 478 func (req *CellReq) PushInsertCell(target CellID, schema *AttrSchema) { 479 if schema != nil { 480 m := NewMsg() 481 m.CellID = target.U64() 482 m.Op = MsgOp_InsertChildCell 483 m.ValType = int32(ValType_SchemaID) 484 m.ValInt = int64(schema.SchemaID) 485 req.PushUpdate(m) 486 } 487 } 488 489 // Pushes the given attr to the client 490 func (req *CellReq) PushAttr(target CellID, schema *AttrSchema, attrURI string, val Value) { 491 attr := schema.LookupAttr(attrURI) 492 if attr == nil { 493 return 494 } 495 496 m := NewMsg() 497 m.CellID = target.U64() 498 m.Op = MsgOp_PushAttr 499 m.AttrID = attr.AttrID 500 if attr.SeriesType == SeriesType_Fixed { 501 m.SI = attr.BoundSI 502 } 503 val.MarshalToMsg(m) 504 if attr.ValTypeID != 0 { // what is this for!? 505 m.ValType = int32(attr.ValTypeID) 506 } 507 req.PushUpdate(m) 508 } 509 510 func (req *CellReq) PushCheckpoint(err error) { 511 m := NewMsg() 512 m.Op = MsgOp_Commit 513 m.CellID = req.PinCell.U64() 514 if err != nil { 515 m.setVal(err) 516 } 517 req.PushUpdate(m) 518 } 519 520 */