From facb7ac062f7f837d9078dceb631dafaa573bf48 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 4 Dec 2013 23:53:00 +0400 Subject: [PATCH] strutil: add StringFromBytes Change-Id: Ie396dd58c48a724e044ecd4f8dc28712e7091f9f --- pkg/strutil/intern.go | 51 ++++++++++++++++ pkg/strutil/strconv.go | 117 ++++++++++++++++++++++++++++++++++++ pkg/strutil/strutil_test.go | 9 +++ 3 files changed, 177 insertions(+) create mode 100644 pkg/strutil/intern.go create mode 100644 pkg/strutil/strconv.go diff --git a/pkg/strutil/intern.go b/pkg/strutil/intern.go new file mode 100644 index 000000000..de1acb4e9 --- /dev/null +++ b/pkg/strutil/intern.go @@ -0,0 +1,51 @@ +/* +Copyright 2013 The Camlistore Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package strutil + +// StringFromBytes returns string(v), minimizing copies for common values of v. +func StringFromBytes(v []byte) string { + // From net/textproto's reader.go... + if len(v) == 0 { + return "" + } + lo, hi := 0, len(commonStrings) + for i, c := range v { + if lo < hi { + for lo < hi && (len(commonStrings[lo]) <= i || commonStrings[lo][i] < c) { + lo++ + } + for hi > lo && commonStrings[hi-1][i] > c { + hi-- + } + } else { + break + } + } + if lo < hi && len(commonStrings[lo]) == len(v) { + return commonStrings[lo] + } + return string(v) +} + +// NOTE: must be sorted +var commonStrings = []string{ + "claim", + "directory", + "file", + "permanode", + "static-set", +} diff --git a/pkg/strutil/strconv.go b/pkg/strutil/strconv.go new file mode 100644 index 000000000..9d4ccffff --- /dev/null +++ b/pkg/strutil/strconv.go @@ -0,0 +1,117 @@ +/* +Copyright 2013 The Camlistore Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package strutil + +import ( + "errors" + "strconv" +) + +// ParseUintBytes is like strconv.ParseUint, but using a []byte. +func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { + var cutoff, maxVal uint64 + + if bitSize == 0 { + bitSize = int(strconv.IntSize) + } + + s0 := s + switch { + case len(s) < 1: + err = strconv.ErrSyntax + goto Error + + case 2 <= base && base <= 36: + // valid base; nothing to do + + case base == 0: + // Look for octal, hex prefix. + switch { + case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): + base = 16 + s = s[2:] + if len(s) < 1 { + err = strconv.ErrSyntax + goto Error + } + case s[0] == '0': + base = 8 + default: + base = 10 + } + + default: + err = errors.New("invalid base " + strconv.Itoa(base)) + goto Error + } + + n = 0 + cutoff = cutoff64(base) + maxVal = 1<= base { + n = 0 + err = strconv.ErrSyntax + goto Error + } + + if n >= cutoff { + // n*base overflows + n = 1<<64 - 1 + err = strconv.ErrRange + goto Error + } + n *= uint64(base) + + n1 := n + uint64(v) + if n1 < n || n1 > maxVal { + // n+v overflows + n = 1<<64 - 1 + err = strconv.ErrRange + goto Error + } + n = n1 + } + + return n, nil + +Error: + return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} +} + +// Return the first number n such that n*base >= 1<<64. +func cutoff64(base int) uint64 { + if base < 2 { + return 0 + } + return (1<<64-1)/uint64(base) + 1 +} diff --git a/pkg/strutil/strutil_test.go b/pkg/strutil/strutil_test.go index 020359eb1..48a2fce0a 100644 --- a/pkg/strutil/strutil_test.go +++ b/pkg/strutil/strutil_test.go @@ -48,3 +48,12 @@ func TestAppendSplitN(t *testing.T) { } } } + +func TestStringFromBytes(t *testing.T) { + for _, s := range []string{"foo", "permanode", "file", "zzzz"} { + got := StringFromBytes([]byte(s)) + if got != s { + t.Errorf("StringFromBytes(%q) didn't round-trip; got %q instead", s, got) + } + } +}