Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Kirill Smelkov
neo
Commits
b1f25716
Commit
b1f25716
authored
Mar 27, 2017
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
1380e37e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
120 additions
and
53 deletions
+120
-53
t/neo/xcommon/xfmt/fmt.go
t/neo/xcommon/xfmt/fmt.go
+27
-3
t/neo/xcommon/xfmt/fmt_test.go
t/neo/xcommon/xfmt/fmt_test.go
+63
-26
t/neo/xcommon/xfmt/python.go
t/neo/xcommon/xfmt/python.go
+25
-20
t/neo/xcommon/xfmt/python_test.go
t/neo/xcommon/xfmt/python_test.go
+5
-4
No files found.
t/neo/xcommon/xfmt/fmt.go
View file @
b1f25716
// TODO copyright/license
// 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 2, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// 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 provide addons to std fmt and strconv packages with focus on
// Package xfmt provide
s
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
//
// xbuf := xfmt.Buffer{}
// xbuf .S("hello ") .Qs("world") .C(' ') .D(1) .C(' ') .Xb([]byte("data"))
// s := xbuf.Bytes()
//
// xfmt.Buffer can be reused several times via Buffer.Reset() .
package
xfmt
import
(
...
...
@@ -76,7 +98,7 @@ func (b *Buffer) Cb(c byte) *Buffer {
}
// AppendRune appends to b
e
UTF-8 encoding of r
// AppendRune appends to b UTF-8 encoding of r
func
AppendRune
(
b
[]
byte
,
r
rune
)
[]
byte
{
l
:=
len
(
b
)
b
=
xslice
.
Grow
(
b
,
utf8
.
UTFMax
)
...
...
@@ -142,3 +164,5 @@ func (b *Buffer) X016(x uint64) *Buffer {
*
b
=
AppendHex016
(
*
b
,
x
)
return
b
}
// TODO Qs Qb ?
t/neo/xcommon/xfmt/fmt_test.go
View file @
b1f25716
...
...
@@ -8,29 +8,30 @@ import (
"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
// TODO %q qb qr qs qcb qc
{
"%s"
,
"S"
,
"hello"
},
{
"%s"
,
"Sb"
,
[]
byte
(
"world"
)},
{
"%x"
,
"Xb"
,
[]
byte
(
"hexstring"
)},
{
"%x"
,
"Xs"
,
"stringhex"
},
{
"%d"
,
"D"
,
12765
},
{
"%x"
,
"X"
,
12789
},
{
"%016x"
,
"X016"
,
uint64
(
124
)},
// TODO .V
}
// verify formatting result is the same in between std fmt and xfmt
func
TestXFmt
(
t
*
testing
.
T
)
{
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
// TODO %q qb qr qs qcb qc
{
"%s"
,
"S"
,
"hello"
},
{
"%s"
,
"Sb"
,
[]
byte
(
"world"
)},
{
"%x"
,
"Xb"
,
[]
byte
(
"hexstring"
)},
{
"%x"
,
"Xs"
,
"stringhex"
},
{
"%d"
,
"D"
,
12765
},
{
"%x"
,
"X"
,
12789
},
{
"%016x"
,
"X016"
,
uint64
(
124
)},
// TODO .V
}
buf
:=
&
Buffer
{}
xbuf
:=
reflect
.
ValueOf
(
buf
)
...
...
@@ -81,10 +82,46 @@ func TestXFmt(t *testing.T) {
}
}
func
BenchmarkFmt
(
t
*
testing
.
T
)
{
// TODO
}
func
BenchmarkXFmt
(
b
*
testing
.
B
)
{
buf
:=
&
Buffer
{}
func
BenchmarkXFmt
(
t
*
testing
.
T
)
{
// TODO
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
uint64
:
methProxy
=
func
(
buf
*
Buffer
,
v
interface
{})
{
meth
.
(
func
(
*
Buffer
,
uint64
)
*
Buffer
)(
buf
,
v
.
(
uint64
))
}
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
)
}
})
}
}
t/neo/xcommon/xfmt/python.go
View file @
b1f25716
package
x
mf
t
package
x
fm
t
import
(
"bytes"
...
...
@@ -9,20 +9,25 @@ import (
)
const
hex
=
"0123456789abcdef"
// pyQuote quotes string the way python repr(str) would do
func
pyQuote
(
s
string
)
string
{
out
:=
pyQuoteBytes
(
mem
.
Bytes
(
s
))
return
mem
.
String
(
out
)
}
func
pyQuoteBytes
(
b
[]
byte
)
[]
byte
{
buf
:=
make
([]
byte
,
0
,
(
len
(
b
)
+
2
)
/* to reduce allocations when quoting */
*
2
)
return
pyAppendQuoteBytes
(
buf
,
b
)
// TODO remove - not needed ?
// // pyQuote quotes string the way python repr(str) would do
// func pyQuote(s string) string {
// out := pyQuoteBytes(mem.Bytes(s))
// return mem.String(out)
// }
//
// func pyQuoteBytes(b []byte) []byte {
// buf := make([]byte, 0, (len(b) + 2) /* to reduce allocations when quoting */ * 2)
// return pyAppendQuoteBytes(buf, b)
// }
// AppendQuotePy appends to buf Python quoting of s
func
AppendQuotePy
(
buf
[]
byte
,
s
string
)
[]
byte
{
return
AppendQuotePyBytes
(
buf
,
mem
.
Bytes
(
s
))
}
func
pyAppendQuoteBytes
(
buf
,
b
[]
byte
)
[]
byte
{
// 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
(
'\'
'
)
...
...
@@ -38,7 +43,7 @@ func pyAppendQuoteBytes(buf, b []byte) []byte {
switch
r
{
case
utf8
.
RuneError
:
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
[
b
[
0
]
>>
4
],
hex
[
b
[
0
]
&
0xf
])
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
digits
[
b
[
0
]
>>
4
],
hexdigits
[
b
[
0
]
&
0xf
])
case
'\\'
,
rune
(
quote
)
:
buf
=
append
(
buf
,
'\\'
,
byte
(
r
))
case
rune
(
noquote
)
:
...
...
@@ -57,7 +62,7 @@ func pyAppendQuoteBytes(buf, b []byte) []byte {
switch
{
case
r
<
' '
:
// we already converted to \<letter> what python represents as such above
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
[
b
[
0
]
>>
4
],
hex
[
b
[
0
]
&
0xf
])
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
digits
[
b
[
0
]
>>
4
],
hexdigits
[
b
[
0
]
&
0xf
])
case
r
<
utf8
.
RuneSelf
/* RuneSelf itself is not printable */
-
1
:
// we already escaped all < RuneSelf runes
...
...
@@ -70,7 +75,7 @@ func pyAppendQuoteBytes(buf, b []byte) []byte {
default
:
// everything else goes in numeric byte escapes
for
i
:=
0
;
i
<
size
;
i
++
{
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
[
b
[
i
]
>>
4
],
hex
[
b
[
i
]
&
0xf
])
buf
=
append
(
buf
,
'\\'
,
'x'
,
hex
digits
[
b
[
i
]
>>
4
],
hexdigits
[
b
[
i
]
&
0xf
])
}
}
}
...
...
@@ -85,12 +90,12 @@ func pyAppendQuoteBytes(buf, b []byte) []byte {
// Qpy appends string quoted as Python would do
func
(
b
*
Buffer
)
Qpy
(
s
string
)
*
Buffer
{
*
b
=
...
// TODO
*
b
=
AppendQuotePy
(
*
b
,
s
)
return
b
}
// Q
pyb
appends []byte quoted as Python would do
func
(
b
*
Buffer
)
Q
pyb
(
x
[]
byte
)
*
Buffer
{
*
b
=
...
// TODO
// Q
bpy
appends []byte quoted as Python would do
func
(
b
*
Buffer
)
Q
bpy
(
x
[]
byte
)
*
Buffer
{
*
b
=
AppendQuotePyBytes
(
*
b
,
x
)
return
b
}
t/neo/xcommon/xfmt/python_test.go
View file @
b1f25716
...
...
@@ -2,8 +2,6 @@ package xfmt
import
(
"testing"
"lab.nexedi.com/kirr/go123/mem"
)
// byterange returns []byte with element [start,stop)
...
...
@@ -45,8 +43,11 @@ var pyQuoteTestv = []struct {in, quoted string} {
}
func
TestPyQuote
(
t
*
testing
.
T
)
{
buf
:=
[]
byte
{}
for
_
,
tt
:=
range
pyQuoteTestv
{
quoted
:=
pyQuote
(
tt
.
in
)
buf
=
buf
[
:
0
]
buf
=
AppendQuotePy
(
buf
,
tt
.
in
)
quoted
:=
string
(
buf
)
if
quoted
!=
tt
.
quoted
{
t
.
Errorf
(
"pyQuote(%q) ->
\n
have: %s
\n
want: %s"
,
tt
.
in
,
quoted
,
tt
.
quoted
)
}
...
...
@@ -59,7 +60,7 @@ func BenchmarkPyQuote(b *testing.B) {
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
for
_
,
tt
:=
range
pyQuoteTestv
{
buf
=
buf
[
:
0
]
buf
=
pyAppendQuoteBytes
(
buf
,
mem
.
Bytes
(
tt
.
in
)
)
buf
=
AppendQuotePy
(
buf
,
tt
.
in
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment