Skip to content

Commit

Permalink
Add ObjectID (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg authored Aug 10, 2023
1 parent 8237093 commit a06e714
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 0 deletions.
87 changes: 87 additions & 0 deletions objectid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package bson

import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
mathrand "math/rand"
"sync/atomic"
"time"
)

var (
_ Marshaler = &ObjectID{}
_ Unmarshaler = &ObjectID{}
)

var ErrBadObjectID = errors.New("provided input is not a valid ObjectID")

// ObjectID represents BSON object ID.
type ObjectID [12]byte

// NewObjectID returns a new ObjectID.
func NewObjectID() ObjectID {
return NewObjectIDWithTime(time.Now())
}

// NewObjectIDWithTime returns a new ObjectID.
func NewObjectIDWithTime(t time.Time) ObjectID {
ts := uint32(t.UTC().Unix())
c := objectIDCounter.Add(1)

var oid ObjectID
oid[0] = byte(ts >> 24)
oid[1] = byte(ts >> 16)
oid[2] = byte(ts >> 8)
oid[3] = byte(ts)

oid[4] = procUniqueID[0]
oid[5] = procUniqueID[1]
oid[6] = procUniqueID[2]
oid[7] = procUniqueID[3]
oid[8] = procUniqueID[4]

oid[9] = byte(c >> 16)
oid[10] = byte(c >> 8)
oid[11] = byte(c)
return oid
}

// String returns a hex string representation of the id.
// Example: ObjectIdHex('64d526fa37931c1e97eea90f').
func (oid ObjectID) String() string {
return fmt.Sprintf(`ObjectIdHex('%x')`, string(oid[:]))
}

func (oid *ObjectID) MarshalBSON() ([]byte, error) {
b := make([]byte, len(oid))
copy(b, oid[:])
return b, nil
}

func (oid *ObjectID) UnmarshalBSON(b []byte) error {
switch len(b) {
case 12:
copy(oid[:], b)
return nil
case 24:
n, err := hex.Decode(oid[:], b)
if n != 24 {
panic("unreachable")
}
return err
default:
return ErrBadObjectID
}
}

var (
procUniqueID [5]byte
objectIDCounter atomic.Uint32
)

func init() {
must(rand.Read(procUniqueID[:]))
objectIDCounter.Store(mathrand.Uint32())
}
11 changes: 11 additions & 0 deletions objectid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package bson

import (
"testing"
)

func TestObjectID(t *testing.T) {
oid := ObjectID([12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})

mustEqual(t, oid.String(), "ObjectIdHex('0102030405060708090a0b0c')")
}
8 changes: 8 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package bson

func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
11 changes: 11 additions & 0 deletions utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package bson

import "testing"

func mustEqual[T comparable](tb testing.TB, have, want T) {
tb.Helper()

if have != want {
tb.Fatalf("\nhave: %+v\nwant: %+v\n", have, want)
}
}

0 comments on commit a06e714

Please sign in to comment.