diff options
author | Rasmus Dahlberg <rasmus@mullvad.net> | 2022-04-25 00:43:06 +0200 |
---|---|---|
committer | Rasmus Dahlberg <rasmus@mullvad.net> | 2022-04-25 00:43:06 +0200 |
commit | 528a53f7f76f08af5902f4cfa8235380b3434ba0 (patch) | |
tree | 662b7834d5ce15627554e9307a4e00f7364fba11 /pkg/ascii/ascii.go | |
parent | 4fc0ff2ec2f48519ee245d6d7edee1921cb3b8bc (diff) |
drafty types refactor with simple ascii packagergdd/sketch
types.go compiles but that is about it, here be dragons. Pushing so
that we can get an idea of what this refactor would roughly look like.
Diffstat (limited to 'pkg/ascii/ascii.go')
-rw-r--r-- | pkg/ascii/ascii.go | 306 |
1 files changed, 0 insertions, 306 deletions
diff --git a/pkg/ascii/ascii.go b/pkg/ascii/ascii.go deleted file mode 100644 index d91a240..0000000 --- a/pkg/ascii/ascii.go +++ /dev/null @@ -1,306 +0,0 @@ -// package ascii implements an ASCII key-value parser. -// -// The top-most (de)serialize must operate on a struct pointer. A struct may -// contain other structs, in which case all tag names should be unique. Public -// fields without tag names are ignored. Private fields are also ignored. -// -// The supported field types are: -// - struct -// - string (no empty strings) -// - uint64 (only digits in ASCII representation) -// - byte array (only lower-case hex in ASCII representation) -// - slice of uint64 (no empty slices) -// - slice of byte array (no empty slices) -// -// A key must not contain an encoding's end-of-key value. -// A value must not contain an encoding's end-of-value value. -// -// For additional details, please refer to the Sigsum v0 API documentation. -// -package ascii - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "reflect" - "strconv" - "strings" - - "git.sigsum.org/sigsum-go/pkg/hex" -) - -var StdEncoding = NewEncoding("ascii", "=", "\n") - -type Encoding struct { - identifier string - endOfKey string - endOfValue string -} - -func NewEncoding(id, eok, eov string) *Encoding { - return &Encoding{ - identifier: id, - endOfKey: eok, - endOfValue: eov, - } -} - -type someValue struct { - v reflect.Value - ok bool -} - -// Serialize tries to serialize an interface as ASCII key-value pairs -func (e *Encoding) Serialize(w io.Writer, i interface{}) error { - v, err := dereferenceStructPointer(i) - if err != nil { - return err - } - - t := v.Type() - for i := 0; i < t.NumField(); i++ { - switch v.Field(i).Type().Kind() { - case reflect.Struct: - if err := e.Serialize(w, v.Field(i).Addr().Interface()); err != nil { - return err - } - default: - if t.Field(i).PkgPath != "" { - continue // skip private field - } - key, ok := t.Field(i).Tag.Lookup(e.identifier) - if !ok { - continue // skip public field without tag - } - - if strings.Contains(key, e.endOfKey) { - return fmt.Errorf("ascii: key %q contains end-of-key character", key) - } - if err := e.write(w, key, v.Field(i)); err != nil { - return err - } - } - } - return nil -} - -func (e *Encoding) write(w io.Writer, key string, v reflect.Value) error { - t := v.Type() - switch t { - case reflect.TypeOf(uint64(0)): - val := fmt.Sprintf("%d", v.Uint()) - return e.writeOne(w, key, val) - } - - k := t.Kind() - switch k { - case reflect.Array: - if kind := t.Elem().Kind(); kind != reflect.Uint8 { - return fmt.Errorf("ascii: array kind not supported: %v", kind) - } - - arrayLen := v.Len() - array := make([]byte, arrayLen, arrayLen) - for i := 0; i < arrayLen; i++ { - array[i] = uint8(v.Index(i).Uint()) - } - - val := hex.Serialize(array) - return e.writeOne(w, key, val) - - case reflect.Slice: - kind := t.Elem().Kind() - if kind != reflect.Array && kind != reflect.Uint64 { - return fmt.Errorf("ascii: slice kind not supported: %v", kind) - } - if v.Len() == 0 { - return fmt.Errorf("ascii: slice must not be empty") - } - - var err error - for i := 0; i < v.Len(); i++ { - err = e.write(w, key, v.Index(i)) - } - return err - - case reflect.String: - if v.Len() == 0 { - return fmt.Errorf("ascii: string must not be empty") - } - return e.writeOne(w, key, v.String()) - } - - return fmt.Errorf("ascii: unsupported type %v and kind %v", t, k) -} - -func (e *Encoding) writeOne(w io.Writer, key, value string) error { - _, err := w.Write([]byte(key + e.endOfKey + value + e.endOfValue)) - return err -} - -// Deserialize tries to deserialize a buffer of ASCII key-value pairs -func (e *Encoding) Deserialize(r io.Reader, i interface{}) error { - m := make(map[string]*someValue) - if err := e.mapKeys(i, m); err != nil { - return err - } - - buf, err := ioutil.ReadAll(r) - if err != nil { - return fmt.Errorf("ascii: failed reading incoming buffer") - } - - // trim end of buffer so that loop does not run on an empty line - if len(buf) <= len(e.endOfValue) { - return fmt.Errorf("ascii: buffer contains no key-value pair") - } - offset := len(buf) - len(e.endOfValue) - if !bytes.Equal(buf[offset:], []byte(e.endOfValue)) { - return fmt.Errorf("ascii: buffer must end with endOfValue") - } - buf = buf[:offset] - - for _, kv := range bytes.Split(buf, []byte(e.endOfValue)) { - split := bytes.Split(kv, []byte(e.endOfKey)) - if len(split) == 1 { - return fmt.Errorf("ascii: missing key-value pair in %q", string(kv)) - } - - key := string(split[0]) - value := string(bytes.Join(split[1:], nil)) - ref, ok := m[key] - if !ok { - return fmt.Errorf("ascii: unexpected key %q", key) - } - if len(value) == 0 { - fmt.Errorf("ascii: missing value for key %q", key) - } - if err := setKey(ref, key, value); err != nil { - return err - } - } - return requireValues(m) -} - -func (e *Encoding) mapKeys(i interface{}, m map[string]*someValue) error { - v, err := dereferenceStructPointer(i) - if err != nil { - return err - } - - t := v.Type() - for i := 0; i < t.NumField(); i++ { - switch v.Field(i).Type().Kind() { - case reflect.Struct: - i := v.Field(i).Addr().Interface() - e.mapKeys(i, m) // return is always nil - default: - if t.Field(i).PkgPath != "" { - continue // skip private field - } - key, ok := t.Field(i).Tag.Lookup(e.identifier) - if !ok { - continue // skip public field without tag - } - m[key] = &someValue{ - v: v.Field(i), - } - } - } - return nil -} - -func setKey(ref *someValue, key, value string) error { - v := ref.v - if v.Kind() == reflect.Ptr && !v.IsNil() { - v = v.Elem() - } - - t := v.Type() - switch t { - case reflect.TypeOf(uint64(0)): - num, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - - ref.ok = true - v.SetUint(num) - return nil - } - - k := t.Kind() - switch k { - case reflect.Array: - arrayLen := v.Len() - b, err := hex.Deserialize(value) - if err != nil { - return err - } - if len(b) != arrayLen { - return fmt.Errorf("ascii: invalid array size for key %q", key) - } - - ref.ok = true - reflect.Copy(v, reflect.ValueOf(b)) - return nil - - case reflect.Slice: - sliceType := t - kind := sliceType.Elem().Kind() - if kind != reflect.Array && kind != reflect.Uint64 { - return fmt.Errorf("ascii: slice kind not supported: %v", kind) - } - - if v.IsNil() { - v.Set(reflect.MakeSlice(sliceType, 0, 0)) - } - sv := &someValue{ - v: reflect.New(sliceType.Elem()), - } - if err := setKey(sv, key, value); err != nil { - return err - } - - ref.ok = true - v.Set(reflect.Append(v, sv.v.Elem())) - return nil - - case reflect.String: - if len(value) == 0 { - return fmt.Errorf("ascii: string must not be empty") - } - - ref.ok = true - v.SetString(value) - return nil - } - - return fmt.Errorf("ascii: unsupported type %v and kind %v", t, k) -} - -func requireValues(m map[string]*someValue) error { - for k, v := range m { - if !v.ok { - return fmt.Errorf("ascii: missing value for key %q", k) - } - } - return nil -} - -func dereferenceStructPointer(i interface{}) (*reflect.Value, error) { - v := reflect.ValueOf(i) - if v.Kind() != reflect.Ptr { - return nil, fmt.Errorf("ascii: interface value must be pointer") - } - if v.IsNil() { - return nil, fmt.Errorf("ascii: interface value must be non-nil pointer") - } - v = v.Elem() - if v.Type().Kind() != reflect.Struct { - return nil, fmt.Errorf("ascii: interface value must point to struct") - } - return &v, nil -} |