// Package stateobjects keeps values of any size on the bus (novox/hq ADR 0666 §16, ADR 0595 §9): a value over the bus's // limit for one value, or a verb reply or an event over the limit for one message, is put in an object store and only its // reference travels. It is a small package of its own, imported only by the modules that use it: the module library (the // root package) and the process package do not import it, so their importers are not rebuilt when it changes. It names no // seat and no module, and imports no contracts module. package stateobjects import ( "bytes" "encoding/hex" "encoding/json" "fmt" ) // FormVersion is the version of the reference form; a reader refuses a reference of another. const FormVersion = 1 // refField is the one field of a body that is a reference. const refField = "mesh-object" // Ref names an object in an object store: a body that is exactly `{"mesh-object": Ref}` is a reference, and anything // else is a value. type Ref struct { // Store is the object store's name as its reader asks for it: a bundle's local name, or the name a process opens. Store string `json:"store"` // Object is the object's name in that store. Object string `json:"object"` // Size is the object's size in bytes. Size int64 `json:"size"` // Digest is the hex SHA-256 of the object's bytes; a reader checks it after the read. Digest string `json:"sha256"` // Form is FormVersion. Form int `json:"form"` } func (r Ref) valid() bool { if r.Store == "" || r.Object == "" || r.Size < 0 || r.Form != FormVersion || len(r.Digest) != 64 { return false } _, err := hex.DecodeString(r.Digest) return err == nil } // Wrap is the body that holds r. func Wrap(r Ref) []byte { raw, _ := json.Marshal(map[string]Ref{refField: r}) return raw } // ReadRef reads a body as a reference: ok is false for a value, including one that only looks like a reference (another // field beside it, a field it does not know, a digest that is no digest, another form). func ReadRef(body []byte) (Ref, bool) { var outer map[string]json.RawMessage if err := json.Unmarshal(body, &outer); err != nil || len(outer) != 1 { return Ref{}, false } raw, ok := outer[refField] if !ok { return Ref{}, false } dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() var r Ref if err := dec.Decode(&r); err != nil || !r.valid() { return Ref{}, false } return r, true } // errLooksLikeRef refuses a value that would be read back as a reference. var errLooksLikeRef = fmt.Errorf("the value is exactly a reference to an object (a body of the one field %q): it would be read as one", refField)