Commit 3d13a276 authored by Kirill Smelkov's avatar Kirill Smelkov

go/zodb: Stringification and parsing for Tid, Oid, Xid

parent 20d8456c
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package zodb
// formatting and parsing for basic zodb types
import (
"encoding/binary"
"encoding/hex"
"fmt"
"lab.nexedi.com/kirr/go123/xfmt"
"lab.nexedi.com/kirr/go123/xstrings"
)
// String converts tid to string.
//
// Default tid string representation is 16-character hex string, e.g.:
//
// 0285cbac258bf266
//
// See also: ParseTid.
func (tid Tid) String() string {
return string(tid.XFmtString(nil))
}
// String converts oid to string.
//
// Default oid string representation is 16-character hex string, e.g.:
//
// 0000000000000001
//
// See also: ParseOid.
func (oid Oid) String() string {
return string(oid.XFmtString(nil))
}
func (tid Tid) XFmtString(b []byte) []byte {
return xfmt.AppendHex016(b, uint64(tid))
}
func (oid Oid) XFmtString(b []byte) []byte {
return xfmt.AppendHex016(b, uint64(oid))
}
// String converts xid to string.
//
// Default xid string representation is:
//
// - string of at
// - ":"
// - string of oid
//
// e.g.
//
// 0285cbac258bf266:0000000000000001 - oid 1 at first newest transaction changing it with tid <= 0285cbac258bf266
//
// See also: ParseXid.
func (xid Xid) String() string {
return xid.At.String() + ":" + xid.Oid.String()
}
/* TODO reenable?
func (xid Xid) XFmtString(b xfmt.Buffer) xfmt.Buffer {
b .V(xid.At) .C(':') .V(xid.Oid)
}
*/
// parseHex64 decodes 16-character-wide hex-encoded string into uint64
func parseHex64(subj, s string) (uint64, error) {
// XXX -> xfmt ?
// XXX like scanf("%016x") but scanf implicitly skips spaces without giving control to caller and is slower
var b [8]byte
if len(s) != 16 {
return 0, fmt.Errorf("%s %q invalid", subj, s)
}
_, err := hex.Decode(b[:], []byte(s))
if err != nil {
return 0, fmt.Errorf("%s %q invalid", subj, s)
}
return binary.BigEndian.Uint64(b[:]), nil
}
// ParseTid parses tid from string.
//
// See also: Tid.String .
func ParseTid(s string) (Tid, error) {
x, err := parseHex64("tid", s)
return Tid(x), err
}
// ParseOid parses oid from string.
//
// See also: Oid.String .
func ParseOid(s string) (Oid, error) {
x, err := parseHex64("oid", s)
return Oid(x), err
}
// ParseXid parses xid from string.
//
// See also: Xid.String .
func ParseXid(s string) (Xid, error) {
ats, oids, err := xstrings.Split2(s, ":")
if err != nil {
goto Error
}
{
at, err1 := ParseTid(ats)
oid, err2 := ParseOid(oids)
if err1 != nil || err2 != nil {
goto Error
}
return Xid{at, oid}, nil
}
Error:
return Xid{}, fmt.Errorf("xid %q invalid", s)
}
// ParseTidRange parses string of form "<tidmin>..<tidmax>" into tidMin, tidMax pair.
//
// Both <tidmin> and <tidmax> can be empty, in which case defaults 0 and TidMax are used.
//
// XXX also check tidMin < tidMax here? or allow reverse ranges ?
func ParseTidRange(s string) (tidMin, tidMax Tid, err error) {
s1, s2, err := xstrings.Split2(s, "..")
if err != nil {
goto Error
}
tidMin = 0
tidMax = TidMax
if s1 != "" {
tidMin, err = ParseTid(s1)
if err != nil {
goto Error
}
}
if s2 != "" {
tidMax, err = ParseTid(s2)
if err != nil {
goto Error
}
}
return tidMin, tidMax, nil
Error:
return 0, 0, fmt.Errorf("tid range %q invalid", s)
}
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package zodb
import "testing"
// estr returns string corresponding to error or "" for nil
func estr(err error) string {
if err == nil {
return ""
}
return err.Error()
}
func TestParseHex64(t *testing.T) {
var testv = []struct {in string; out uint64; estr string} {
{"", 0, `tid "" invalid`},
{"0123456789abcde", 0, `tid "0123456789abcde" invalid`},
{"0123456789abcdeq", 0, `tid "0123456789abcdeq" invalid`},
{"0123456789abcdef", 0x0123456789abcdef, ""},
}
for _, tt := range testv {
x, err := parseHex64("tid", tt.in)
if !(x == tt.out && estr(err) == tt.estr) {
t.Errorf("parsehex64: %v: test error:\nhave: %v %q\nwant: %v %q", tt.in, x, err, tt.out, tt.estr)
}
}
}
func TestParseXid(t *testing.T) {
var testv = []struct {in string; xid Xid; estr string} {
{"", Xid{}, `xid "" invalid`},
{"a", Xid{}, `xid "a" invalid`},
{"0123456789abcdef", Xid{}, `xid "0123456789abcdef" invalid`},
{"z0123456789abcdef", Xid{}, `xid "z0123456789abcdef" invalid`},
{"=0123456789abcdef", Xid{}, `xid "=0123456789abcdef" invalid`},
{"<0123456789abcdef", Xid{}, `xid "<0123456789abcdef" invalid`},
{"=0123456789abcdef|fedcba9876543210", Xid{}, `xid "=0123456789abcdef|fedcba9876543210" invalid`},
{"<0123456789abcdef|fedcba9876543210", Xid{}, `xid "<0123456789abcdef|fedcba9876543210" invalid`},
{"=0123456789abcdef:fedcba9876543210", Xid{}, `xid "=0123456789abcdef:fedcba9876543210" invalid`},
{"<0123456789abcdef:fedcba9876543210", Xid{}, `xid "<0123456789abcdef:fedcba9876543210" invalid`},
{"0123456789abcdef:fedcba9876543210", Xid{0x0123456789abcdef, 0xfedcba9876543210}, ""},
}
for _, tt := range testv {
xid, err := ParseXid(tt.in)
if !(xid == tt.xid && estr(err) == tt.estr) {
t.Errorf("parsexid: %v: test error:\nhave: %v %q\nwant: %v %q",
tt.in, xid, err, tt.xid, tt.estr)
}
}
}
func TestParseTidRange(t *testing.T) {
var testv = []struct {in string; tidMin, tidMax Tid; estr string} {
{"", 0, 0, `tid range "" invalid`},
{".", 0, 0, `tid range "." invalid`},
{"..", 0, TidMax, ""},
{"0123456789abcdef..", 0x0123456789abcdef, TidMax, ""},
{"..0123456789abcdef", 0, 0x0123456789abcdef, ""},
}
for _, tt := range testv {
tmin, tmax, err := ParseTidRange(tt.in)
estr := ""
if err != nil {
estr = err.Error()
}
if !(tmin == tt.tidMin && tmax == tt.tidMax && estr == tt.estr) {
t.Errorf("parseTidRange: %v: test error:\nhave: %v %v %q\nwant: %v %v %q", tt.in,
tmin, tmax, estr, tt.tidMin, tt.tidMax, tt.estr)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment