diff options
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 -} |