/* NAME meta.go DESCRIPTION See Readme.md AUTHOR Saxon Nelson-Milton LICENSE meta.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package meta import ( "errors" "strings" "sync" ) const headSize = 4 const ( majVer = 1 minVer = 0 ) const ( dataLenIdx1 = 2 dataLenIdx2 = 3 ) var ( errKeyAbsent = errors.New("Key does not exist in map") ) type Metadata struct { mu sync.RWMutex data map[string]string enc []byte } func New() *Metadata { return &Metadata{ data: make(map[string]string), enc: []byte{ 0x00, // Reserved byte (majVer << 4) | minVer, // MS and LS versions 0x00, // Data len byte1 0x00, // Data len byte2 }, } } // Add adds metadata with key and val func (m *Metadata) Add(key, val string) { m.mu.Lock() m.data[key] = val m.mu.Unlock() } // All returns the a copy of the map containing the meta data func (m *Metadata) All() map[string]string { m.mu.Lock() cpy := make(map[string]string) for k, v := range m.data { cpy[k] = v } m.mu.Unlock() return cpy } // Get returns the meta data for the passed key func (m *Metadata) Get(key string) (string, error) { m.mu.Lock() val, ok := m.data[key] m.mu.Unlock() if !ok { return "", errKeyAbsent } return val, nil } // Remove deletes a meta entry in the map and returns error if it doesn’t exist func (m *Metadata) Delete(key string) error { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.data[key]; ok { delete(m.data, key) return nil } return errKeyAbsent } // Encode takes the meta data map and encods into a byte slice with header // describing the version, length of data and data in TSV format. func (m *Metadata) Encode() []byte { m.enc = m.enc[:headSize] // Iterate over map and append entries, only adding tab if we're not on the last entry var i int var entry string for k, v := range m.data { i++ entry += k + "=" + v if i < len(m.data) { entry += "\t" } } m.enc = append(m.enc, []byte(entry)...) // Calculate and set data length in encoded meta header. dataLen := len(m.enc[headSize:]) m.enc[dataLenIdx1] = byte(dataLen >> 8) m.enc[dataLenIdx2] = byte(dataLen) return m.enc } func ReadFrom(d []byte, key string) (string, error) { entries := strings.Split(string(d), "\t") for _, entry := range entries { kv := strings.Split(entry, "=") if kv[0] == key { return kv[1], nil } } return "", errors.New("could not find key in metadata") }