ironsmith/datastore/key.go

127 lines
2.8 KiB
Go

// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package datastore
import (
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"time"
)
// TimeKey is a unique time ordered key for use in the datastore
// A TimeKey is 32 bits random data + 96 bit UNIX timestamp (64bits seconds + 32 bit nanoseconds)
type TimeKey [16]byte
// NewTimeKey returns a newly generated TimeKey based on the current time
func NewTimeKey() TimeKey {
rBits := make([]byte, 32/8)
_, err := io.ReadFull(rand.Reader, rBits)
if err != nil {
panic(fmt.Sprintf("Error generating random values for New TimeKey: %v", err))
}
t := time.Now()
sec := t.Unix()
nsec := t.Nanosecond()
return TimeKey{
byte(sec >> 56), // seconds
byte(sec >> 48),
byte(sec >> 40),
byte(sec >> 32),
byte(sec >> 24),
byte(sec >> 16),
byte(sec >> 8),
byte(sec),
byte(nsec >> 24), // nanoseconds
byte(nsec >> 16),
byte(nsec >> 8),
byte(nsec),
rBits[0], //random
rBits[1],
rBits[2],
rBits[3],
}
}
// Time returns the time portion of a timekey
func (k TimeKey) Time() time.Time {
buf := k[:]
sec := int64(buf[7]) | int64(buf[6])<<8 | int64(buf[5])<<16 | int64(buf[4])<<24 |
int64(buf[3])<<32 | int64(buf[2])<<40 | int64(buf[1])<<48 | int64(buf[0])<<56
buf = buf[8:]
nsec := int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24
return time.Unix(sec, int64(nsec))
}
// UUID returns the string representation of a TimeKey in Hex format separated by dashes
// similar to a UUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
func (k TimeKey) UUID() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], k[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], k[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], k[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], k[8:10])
buf[23] = '-'
hex.Encode(buf[24:], k[10:])
return string(buf)
}
// Bytes returns the a slice of the underlying bytes of a TimeKey
func (k TimeKey) Bytes() []byte {
return []byte(k[:])
}
// String returns the string representation of a timekey
func (k TimeKey) String() string {
return k.UUID()
}
// MarshalJSON implements JSON marshaler
func (k *TimeKey) MarshalJSON() ([]byte, error) {
return []byte(`"` + k.String() + `"`), nil
}
// UnmarshalJSON implements JSON unmarshaler
func (k *TimeKey) UnmarshalJSON(buf []byte) error {
// drop quotes
buf = buf[1 : len(buf)-1]
_, err := hex.Decode(k[0:4], buf[0:8])
if err != nil {
return err
}
_, err = hex.Decode(k[4:6], buf[9:13])
if err != nil {
return err
}
_, err = hex.Decode(k[6:8], buf[14:18])
if err != nil {
return err
}
_, err = hex.Decode(k[8:10], buf[19:23])
if err != nil {
return err
}
_, err = hex.Decode(k[10:], buf[24:])
if err != nil {
return err
}
return nil
}