Commit 5bd2dd34 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent c31c94b5
......@@ -32,8 +32,9 @@ import (
"../../../../storage/fs1"
"../../../../xcommon/xbufio"
"../../../../xcommon/xbytes"
"../../../../xcommon/xfmt"
"lab.nexedi.com/kirr/go123/xbytes"
"lab.nexedi.com/kirr/go123/xfmt"
)
......
......@@ -24,7 +24,8 @@ import (
"../../zodb"
"../../xcommon/xbufio"
"../../xcommon/xbytes"
"lab.nexedi.com/kirr/go123/xbytes"
)
// FileStorage is a ZODB storage which stores data in simple append-only file
......
// 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 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.
// (re)allocation routines for []byte
package xbytes
import (
"../xmath"
)
// Grow increases length of byte slice by n elements.
// If there is not enough capacity the slice is reallocated and copied.
// The memory for grown elements is not initialized.
func Grow(b []byte, n int) []byte {
ln := len(b) + n
if ln <= cap(b) {
return b[:ln]
}
bb := make([]byte, ln, xmath.CeilPow2(uint64(ln)))
copy(bb, b)
return bb
}
// MakeRoom makes sure cap(b) - len(b) >= n
// If there is not enough capacity the slice is reallocated and copied.
// Length of the slice remains unchanged.
func MakeRoom(b []byte, n int) []byte {
ln := len(b) + n
if ln <= cap(b) {
return b
}
bb := make([]byte, len(b), xmath.CeilPow2(uint64(ln)))
copy(bb, b)
return bb
}
// Resize resized byte slice to be of length n.
// If slice length is increased and there is not enough capacity the slice is reallocated and copied.
// The memory for grown elements, if any, is not initialized.
func Resize(b []byte, n int) []byte {
if cap(b) >= n {
return b[:n]
}
bb := make([]byte, n, xmath.CeilPow2(uint64(n)))
copy(bb, b)
return bb
}
// Realloc resizes byte slice to be of length n not preserving content.
// If slice length is increased and there is not enough capacity the slice is reallocated but not copied.
// The memory for all elements becomes uninitialized.
//
// NOTE semantic is different from C realloc(3) where content is preserved
// NOTE use Resize when you need to preserve slice content
func Realloc(b []byte, n int) []byte {
return Realloc64(b, int64(n))
}
// Realloc64 is the same as Realloc but for size typed as int64
func Realloc64(b []byte, n int64) []byte {
if int64(cap(b)) >= n {
return b[:n]
}
return make([]byte, n, xmath.CeilPow2(uint64(n)))
}
// 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 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.
package xbytes
import (
"bytes"
"reflect"
"testing"
"unsafe"
)
// aliases returns whether two slice memory is aliased
func aliases(b1, b2 []byte) bool {
s1 := (*reflect.SliceHeader)(unsafe.Pointer(&b1))
s2 := (*reflect.SliceHeader)(unsafe.Pointer(&b2))
return s1.Data == s2.Data
}
func TestSlice(t *testing.T) {
s := make([]byte, 0, 10)
testv := []struct {op func([]byte, int) []byte; n, Len, Cap int; aliased bool; content []byte} {
// op, n, Len, Cap, aliased, content
{Grow, 5, 5, 10, true, []byte{0,0,0,0,0}},
// here "Hello" is assigned
{Grow, 6, 11, 16, false, []byte("Hello\x00\x00\x00\x00\x00\x00")},
{MakeRoom, 4, 11, 16, true, []byte("Hello\x00\x00\x00\x00\x00\x00")},
{MakeRoom, 6, 11, 32, false, []byte("Hello\x00\x00\x00\x00\x00\x00")},
{Resize, 8, 8, 32, true, []byte("Hello\x00\x00\x00")},
{Resize, 33, 33, 64, false, append([]byte("Hello"), bytes.Repeat([]byte{0}, 33-5)...)},
{Realloc, 16, 16, 64, true, []byte("Hello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
{Realloc, 65, 65, 128, false, make([]byte, 65)},
}
for i, tt := range testv {
sprev := s
s = tt.op(s, tt.n)
if !(len(s) == tt.Len && cap(s) == tt.Cap && bytes.Equal(s, tt.content)) {
t.Fatalf("step %d: %v: unexpected slice state: %v (cap: %v)", i, tt, s, cap(s))
}
if !(aliases(s, sprev) == tt.aliased) {
t.Fatalf("step %d: %v: unexpected slice aliasing: %v", aliases(s, sprev))
}
// assign data after first iteration
if i == 0 {
copy(s, "Hello")
}
}
}
// 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 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.
// Package xbytes provides additional utilities for working with byte slices
package xbytes
import (
"bytes"
)
// ContainsByte is like bytes.ContainsRune but a bit faster
func ContainsByte(s []byte, c byte) bool {
return bytes.IndexByte(s, c) >= 0
}
// 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 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.
// Package xfmt provides addons to std fmt and strconv packages with focus on
// formatting text without allocations.
//
// For example if in fmt speak you have
//
// s := fmt.Sprintf("hello %q %d %x", "world", 1, []byte("data"))
//
// xfmt analog would be
//
// buf := xfmt.Buffer{}
// buf .S("hello ") .Q("world") .C(' ') .D(1) .C(' ') .Xb([]byte("data"))
// s := buf.Bytes()
//
// xfmt.Buffer can be reused several times via Buffer.Reset() .
package xfmt
import (
"encoding/hex"
"strconv"
"unicode/utf8"
"../xbytes"
"lab.nexedi.com/kirr/go123/mem"
)
const (
hexdigits = "0123456789abcdef"
)
// Stringer is interface for natively formatting a value representation via xfmt
type Stringer interface {
// XFmtString method is used to append formatted value to destination buffer
// The grown buffer have to be returned
XFmtString(b []byte) []byte
}
// Buffer provides syntactic sugar for formatting mimicking fmt.Printf style
// XXX combine with bytes.Buffer ?
type Buffer []byte
// Reset empties the buffer keeping underlying storage for future formattings
func (b *Buffer) Reset() {
*b = (*b)[:0]
}
// Bytes returns buffer storage as []byte
func (b Buffer) Bytes() []byte {
return []byte(b)
}
// Append appends to b formatted x
//
// NOTE sadly since x is interface it makes real value substituted to it
// escape to heap (not so a problem since usually they already are) but then also
// if x has non-pointer receiver convT2I _allocates_ memory for the value copy.
//
// -> always pass to append &object, even if object has non-pointer XFmtString receiver.
func Append(b []byte, x Stringer) []byte {
return x.XFmtString(b)
}
// V, similarly to %v, adds x formatted by default rules
func (b *Buffer) V(x Stringer) *Buffer {
*b = Append(*b, x)
return b
}
// S appends string formatted by %s
func (b *Buffer) S(s string) *Buffer {
*b = append(*b, s...)
return b
}
// Sb appends []byte formatted by %s
func (b *Buffer) Sb(x []byte) *Buffer {
*b = append(*b, x...)
return b
}
// Q appends string formatted by %q
func (b *Buffer) Q(s string) *Buffer {
*b = strconv.AppendQuote(*b, s)
return b
}
// Qb appends []byte formatted by %q
func (b *Buffer) Qb(s []byte) *Buffer {
*b = strconv.AppendQuote(*b, mem.String(s))
return b
}
// Qcb appends byte formatted by %q
func (b *Buffer) Qcb(c byte) *Buffer {
return b.Qc(rune(c))
}
// Qc appends rune formatted by %q
func (b *Buffer) Qc(c rune) *Buffer {
*b = strconv.AppendQuoteRune(*b, c)
return b
}
// Cb appends byte formatted by %c
func (b *Buffer) Cb(c byte) *Buffer {
*b = append(*b, c)
return b
}
// AppendRune appends to b UTF-8 encoding of r
func AppendRune(b []byte, r rune) []byte {
l := len(b)
b = xbytes.Grow(b, utf8.UTFMax)
n := utf8.EncodeRune(b[l:], r)
return b[:l+n]
}
// C appends rune formatted by %c
func (b *Buffer) C(r rune) *Buffer {
*b = AppendRune(*b, r)
return b
}
// D appends int formatted by %d
func (b *Buffer) D(i int) *Buffer {
*b = strconv.AppendInt(*b, int64(i), 10)
return b
}
// D64 appends int64 formatted by %d
func (b *Buffer) D64(i int64) *Buffer {
*b = strconv.AppendInt(*b, i, 10)
return b
}
// X appends int formatted by %x
func (b *Buffer) X(i int) *Buffer {
*b = strconv.AppendInt(*b, int64(i), 16)
return b
}
// AppendHex appends to b hex representation of x
func AppendHex(b []byte, x []byte) []byte {
lx := hex.EncodedLen(len(x))
lb := len(b)
b = xbytes.Grow(b, lx)
hex.Encode(b[lb:], x)
return b
}
// Xb appends []byte formatted by %x
func (b *Buffer) Xb(x []byte) *Buffer {
*b = AppendHex(*b, x)
return b
}
// Xs appends string formatted by %x
func (b *Buffer) Xs(x string) *Buffer {
return b.Xb(mem.Bytes(x))
}
// TODO XX = %X
// AppendHex016 appends to b x formatted 16-character hex string
func AppendHex016(b []byte, x uint64) []byte {
// like sprintf("%016x") but faster and less allocations
l := len(b)
b = xbytes.Grow(b, 16)
bb := b[l:]
for i := 15; i >= 0; i-- {
bb[i] = hexdigits[x & 0xf]
x >>= 4
}
return b
}
// X016, similarly to %016x, adds hex representation of uint64 x
func (b *Buffer) X016(x uint64) *Buffer {
*b = AppendHex016(*b, x)
return b
}
// 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 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.
package xfmt
import (
"fmt"
"reflect"
"testing"
)
var testv = []struct {format, xformatMeth string; value interface{}} {
{"%c", "Cb", byte('A')},
{"%c", "C", rune(-1)},
{"%c", "C", 'B'}, // 1-byte encoded
{"%c", "C", 'и'}, // 2-bytes encoded
{"%c", "C", '\u20ac'}, // 3-bytes encoded
{"%c", "C", '\U00010001'}, // 4-bytes encoded
{"%s", "S", "hello"},
{"%s", "Sb", []byte("world")},
{"%q", "Q", "alpha"},
{"%q", "Qb", []byte("beta")},
{"%q", "Qcb", byte('D')},
{"%q", "Qc", 'B'}, // 1-byte encoded
{"%q", "Qc", 'и'}, // 2-bytes encoded
{"%q", "Qc", '\u20ac'}, // 3-bytes encoded
{"%q", "Qc", '\U00010001'}, // 4-bytes encoded
{"%x", "Xb", []byte("hexstring")},
{"%x", "Xs", "stringhex"},
{"%d", "D", 12765},
{"%d", "D64", int64(12764)},
{"%x", "X", 12789},
{"%016x", "X016", uint64(124)},
{"%v", "V", &stringerTest{}},
}
type stringerTest struct {
}
func (s *stringerTest) String() string {
return string(s.XFmtString(nil))
}
func (s *stringerTest) XFmtString(b []byte) []byte {
return append(b, `stringer test`...)
}
// verify formatting result is the same in between std fmt and xfmt
func TestXFmt(t *testing.T) {
buf := &Buffer{}
xbuf := reflect.ValueOf(buf)
for _, tt := range testv {
// result via fmt
resFmt := fmt.Sprintf(tt.format, tt.value)
// result via xfmt (via reflect.Call)
buf.Reset()
xmeth := xbuf.MethodByName(tt.xformatMeth)
if !xmeth.IsValid() {
t.Errorf(".%v: no such method", tt.xformatMeth)
continue
}
xargv := []reflect.Value{reflect.ValueOf(tt.value)}
xretv := []reflect.Value{}
callOk := false
func () {
defer func() {
if r := recover(); r != nil {
t.Errorf("%v: panic: %v", tt, r)
}
}()
xretv = xmeth.Call(xargv)
callOk = true
}()
if !callOk {
continue
}
// check all formatters return pointer to the same buf
// (this way it is handy to do .S("hello ") .X016(123) .V(zzz) ...
if !(len(xretv) == 1 && xretv[0].Interface() == buf) {
t.Errorf(".%v: returned %#v ; want %#v", tt.xformatMeth, xretv[0].Interface(), buf)
continue
}
resXFmt := string(*buf)
// results must be the same
if resFmt != resXFmt {
t.Errorf(".%v(%v) -> %q != printf(%q) -> %q",
tt.xformatMeth, tt.value, resXFmt, tt.format, resFmt)
}
}
}
func BenchmarkXFmt(b *testing.B) {
buf := &Buffer{}
for _, tt := range testv {
b.Run(fmt.Sprintf("%s(%#v)", tt.format, tt.value), func (b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf(tt.format, tt.value)
}
})
// construct methProxy for natively calling (not via reflect) method associated with tt.xformatMeth
// (calling via reflect allocates a lot and is slow)
// NOTE because of proxies the call is a bit slower than e.g. directly calling buf.S("...")
var methProxy func(buf *Buffer, v interface{})
xmeth, ok := reflect.TypeOf(buf).MethodByName(tt.xformatMeth)
if !ok {
b.Errorf(".%v: no such method", tt.xformatMeth)
continue
}
// XXX a bit ugly -> use code generation instead?
meth := xmeth.Func.Interface()
switch tt.value.(type) {
case byte: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, byte) *Buffer)(buf, v.(byte)) }
case rune: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, rune) *Buffer)(buf, v.(rune)) }
case string: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, string) *Buffer)(buf, v.(string)) }
case []byte: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, []byte) *Buffer)(buf, v.([]byte)) }
case int: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, int) *Buffer)(buf, v.(int)) }
case int64: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, int64) *Buffer)(buf, v.(int64)) }
case uint64: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, uint64) *Buffer)(buf, v.(uint64)) }
case *stringerTest: methProxy = func(buf *Buffer, v interface{}) { meth.(func (*Buffer, Stringer) *Buffer)(buf, v.(Stringer)) }
default:
b.Fatalf("TODO add support for %T", tt.value)
}
b.Run(fmt.Sprintf(".%s(%#v)", tt.xformatMeth, tt.value), func (b *testing.B) {
for i := 0; i < b.N; i++ {
buf.Reset()
methProxy(buf, tt.value)
}
})
}
}
// 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 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.
// quoting by Python rules
package xfmt
import (
"strconv"
"unicode/utf8"
"lab.nexedi.com/kirr/go123/mem"
"../xbytes"
)
// AppendQuotePy appends to buf Python quoting of s
func AppendQuotePy(buf []byte, s string) []byte {
return AppendQuotePyBytes(buf, mem.Bytes(s))
}
// AppendQuotePyBytes appends to buf Python quoting of b
func AppendQuotePyBytes(buf, b []byte) []byte {
// smartquotes: choose ' or " as quoting character
// https://github.com/python/cpython/blob/v2.7.13-116-g1aa1803b3d/Objects/stringobject.c#L947
quote := byte('\'')
if xbytes.ContainsByte(b, '\'') && !xbytes.ContainsByte(b, '"') {
quote = '"'
}
buf = append(buf, quote)
for i := 0; i < len(b); {
c := b[i]
switch {
// fast path - ASCII only - trying to avoid UTF-8 decoding
case c < utf8.RuneSelf:
switch {
case c == '\\' || c == quote:
buf = append(buf, '\\', c)
case ' ' <= c && c <= '\x7e':
// printable ASCII
buf = append(buf, c)
// below: non-printable ASCII
// NOTE python converts to \<letter> only \t \n \r (not e.g. \v)
// https://github.com/python/cpython/blob/v2.7.13-116-g1aa1803b3d/Objects/stringobject.c#L963
case c == '\t':
buf = append(buf, `\t`...)
case c == '\n':
buf = append(buf, `\n`...)
case c == '\r':
buf = append(buf, `\r`...)
default:
// NOTE c < ' ' or c == '\x7f' (the only non-printable ASCII character > space) here
// we already converted to \<letter> what python represents as such above
// everything else goes in numeric byte escapes
buf = append(buf, '\\', 'x', hexdigits[c>>4], hexdigits[c&0xf])
}
i++
// slow path - full UTF-8 decoding
default:
r, size := utf8.DecodeRune(b[i:])
isize := i + size
switch {
case r == utf8.RuneError:
// decode error - just emit raw byte as escaped
buf = append(buf, '\\', 'x', hexdigits[c>>4], hexdigits[c&0xf])
case strconv.IsPrint(r):
// printable utf-8 characters go as is
buf = append(buf, b[i:isize]...)
default:
// everything else goes in numeric byte escapes
for j := i; j < isize; j++ {
buf = append(buf, '\\', 'x', hexdigits[b[j]>>4], hexdigits[b[j]&0xf])
}
}
i = isize
}
}
buf = append(buf, quote)
return buf
}
// Qpy appends string quoted as Python would do
func (b *Buffer) Qpy(s string) *Buffer {
*b = AppendQuotePy(*b, s)
return b
}
// Qpyb appends []byte quoted as Python would do
func (b *Buffer) Qpyb(x []byte) *Buffer {
*b = AppendQuotePyBytes(*b, x)
return b
}
// 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 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.
package xfmt
import (
"testing"
)
// byterange returns []byte with element [start,stop)
func byterange(start, stop byte) []byte {
b := make([]byte, 0, stop-start)
for ; start < stop; start++ {
b = append(b, start)
}
return b
}
var pyQuoteTestv = []struct {in, quoted string} {
// empty
{``, `''`},
// special characters
{string(byterange(0, 32)), `'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f'`},
// " vs '
{`hello world`, `'hello world'`},
{`hello ' world`, `"hello ' world"`},
{`hello ' " world`, `'hello \' " world'`},
// \
{`hello \ world`, `'hello \\ world'`},
// utf-8
// XXX python escapes non-ascii, but since FileStorage connot
// commit such strings we take the freedom and output them as
// readable.
//{`привет мир`, `'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbc\xd0\xb8\xd1\x80'`},
{`привет мир`, `'привет мир'`},
// invalid utf-8
{"\xd0a", `'\xd0a'`},
// non-printable utf-8
{"\u007f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087", `'\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83\xc2\x84\xc2\x85\xc2\x86\xc2\x87'`},
}
func TestPyQuote(t *testing.T) {
buf := []byte{}
for _, tt := range pyQuoteTestv {
buf = buf[:0]
buf = AppendQuotePy(buf, tt.in)
quoted := string(buf)
if quoted != tt.quoted {
t.Errorf("pyQuote(%q) ->\nhave: %s\nwant: %s", tt.in, quoted, tt.quoted)
}
}
}
func BenchmarkPyQuote(b *testing.B) {
buf := []byte{}
for i := 0; i < b.N; i++ {
for _, tt := range pyQuoteTestv {
buf = buf[:0]
buf = AppendQuotePy(buf, tt.in)
}
}
}
// 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 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.
// XXX check on go18
// Package xmath provides addons to std math package
package xmath
import (
"math/bits"
)
// CeilPow2 returns minimal y >= x, such that y = 2^i
// XXX naming to reflect it works on uint64 ? CeilPow264 looks ambigous
func CeilPow2(x uint64) uint64 {
switch bits.OnesCount64(x) {
case 0, 1:
return x // either 0 or 2^i already
default:
return 1 << uint(bits.Len64(x))
}
}
// XXX if needed: NextPow2 (y > x, such that y = 2^i) is
// 1 << bits.Len64(x)
// 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 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.
package xmath
import (
"testing"
)
func TestCeilPow2(t *testing.T) {
testv := []struct {in, out uint64} {
{0, 0},
{1, 1},
{2, 2},
{3, 4},
{4, 4},
{5, 8},
{5, 8},
{6, 8},
{7, 8},
{8, 8},
{9, 16},
{10, 16},
{11, 16},
{12, 16},
{13, 16},
{14, 16},
{15, 16},
{16, 16},
{1<<62 - 1, 1<<62},
{1<<62, 1<<62},
{1<<62+1, 1<<63},
{1<<63 - 1, 1<<63},
{1<<63, 1<<63},
}
for _, tt := range testv {
out := CeilPow2(tt.in)
if out != tt.out {
t.Errorf("CeilPow(%v) -> %v ; want %v", tt.in, out, tt.out)
}
}
}
......@@ -39,9 +39,10 @@ import (
"log"
"os"
"lab.nexedi.com/kirr/go123/xfmt"
"../../../zodb"
"../../../storage/fs1"
"../../../xcommon/xfmt"
)
......
......@@ -9,9 +9,8 @@ import (
"encoding/hex"
"encoding/binary"
"lab.nexedi.com/kirr/go123/xfmt"
"lab.nexedi.com/kirr/go123/xstrings"
"../xcommon/xfmt"
)
func (tid Tid) String() string {
......
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