Skip to content

Commit

Permalink
Merge pull request #22 from hermannm/remove-dependencies
Browse files Browse the repository at this point in the history
remove redundant dependencies
  • Loading branch information
neilotoole authored Nov 10, 2023
2 parents 09171c9 + f556b90 commit ab3db80
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 27 deletions.
124 changes: 124 additions & 0 deletions ascii.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package jsoncolor

import "unsafe"

// asciiValid returns true if b contains only ASCII characters.
//
// From https://github.com/segmentio/encoding/blob/v0.1.14/ascii/valid.go#L28
//
//go:nosplit
func asciiValid(b []byte) bool {
s, n := unsafe.Pointer(&b), uintptr(len(b))

i := uintptr(0)
p := *(*unsafe.Pointer)(s)

for n >= 8 {
if ((*(*uint64)(unsafe.Pointer(uintptr(p) + i))) & 0x8080808080808080) != 0 {
return false
}
i += 8
n -= 8
}

if n >= 4 {
if ((*(*uint32)(unsafe.Pointer(uintptr(p) + i))) & 0x80808080) != 0 {
return false
}
i += 4
n -= 4
}

var x uint32
switch n {
case 3:
x = uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i))) | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i + 1)))<<8
case 2:
x = uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i)))
case 1:
x = uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i)))
default:
return true
}
return (x & 0x80808080) == 0
}

// asciiValidPrint returns true if b contains only printable ASCII characters.
//
// From https://github.com/segmentio/encoding/blob/v0.1.14/ascii/valid.go#L83
//
//go:nosplit
func asciiValidPrint(b []byte) bool {
s, n := unsafe.Pointer(&b), uintptr(len(b))

if n == 0 {
return true
}

i := uintptr(0)
p := *(*unsafe.Pointer)(s)

for (n - i) >= 8 {
x := *(*uint64)(unsafe.Pointer(uintptr(p) + i))
if hasLess64(x, 0x20) || hasMore64(x, 0x7e) {
return false
}
i += 8
}

if (n - i) >= 4 {
x := *(*uint32)(unsafe.Pointer(uintptr(p) + i))
if hasLess32(x, 0x20) || hasMore32(x, 0x7e) {
return false
}
i += 4
}

var x uint32
switch n - i {
case 3:
x = 0x20000000 | uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i))) | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i + 1)))<<8
case 2:
x = 0x20200000 | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i)))
case 1:
x = 0x20202000 | uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i)))
default:
return true
}
return !(hasLess32(x, 0x20) || hasMore32(x, 0x7e))
}

// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
const (
hasLessConstL64 = (^uint64(0)) / 255
hasLessConstR64 = hasLessConstL64 * 128

hasLessConstL32 = (^uint32(0)) / 255
hasLessConstR32 = hasLessConstL32 * 128

hasMoreConstL64 = (^uint64(0)) / 255
hasMoreConstR64 = hasMoreConstL64 * 128

hasMoreConstL32 = (^uint32(0)) / 255
hasMoreConstR32 = hasMoreConstL32 * 128
)

//go:nosplit
func hasLess64(x, n uint64) bool {
return ((x - (hasLessConstL64 * n)) & ^x & hasLessConstR64) != 0
}

//go:nosplit
func hasLess32(x, n uint32) bool {
return ((x - (hasLessConstL32 * n)) & ^x & hasLessConstR32) != 0
}

//go:nosplit
func hasMore64(x, n uint64) bool {
return (((x + (hasMoreConstL64 * (127 - n))) | x) & hasMoreConstR64) != 0
}

//go:nosplit
func hasMore32(x, n uint32) bool {
return (((x + (hasMoreConstL32 * (127 - n))) | x) & hasMoreConstR32) != 0
}
65 changes: 65 additions & 0 deletions ascii_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package jsoncolor

import (
"strings"
"testing"
)

// Based on https://github.com/segmentio/encoding/blob/v0.1.14/ascii/valid_test.go
var testCases = [...]struct {
valid bool
validPrint bool
str string
}{
{valid: true, validPrint: true, str: ""},
{valid: true, validPrint: true, str: "hello"},
{valid: true, validPrint: true, str: "Hello World!"},
{valid: true, validPrint: true, str: "Hello\"World!"},
{valid: true, validPrint: true, str: "Hello\\World!"},
{valid: true, validPrint: false, str: "Hello\nWorld!"},
{valid: true, validPrint: false, str: "Hello\rWorld!"},
{valid: true, validPrint: false, str: "Hello\tWorld!"},
{valid: true, validPrint: false, str: "Hello\bWorld!"},
{valid: true, validPrint: false, str: "Hello\fWorld!"},
{valid: true, validPrint: true, str: "H~llo World!"},
{valid: true, validPrint: true, str: "H~llo"},
{valid: false, validPrint: false, str: "你好"},
{valid: true, validPrint: true, str: "~"},
{valid: false, validPrint: false, str: "\x80"},
{valid: true, validPrint: false, str: "\x7F"},
{valid: false, validPrint: false, str: "\xFF"},
{valid: true, validPrint: true, str: "some kind of long string with only ascii characters."},
{valid: false, validPrint: false, str: "some kind of long string with a non-ascii character at the end.\xff"},
{valid: true, validPrint: true, str: strings.Repeat("1234567890", 1000)},
}

func TestAsciiValid(t *testing.T) {
for _, tc := range testCases {
t.Run(limit(tc.str), func(t *testing.T) {
expect := tc.validPrint

if valid := asciiValidPrint([]byte(tc.str)); expect != valid {
t.Errorf("expected %t but got %t", expect, valid)
}
})
}
}

func TestAsciiValidPrint(t *testing.T) {
for _, tc := range testCases {
t.Run(limit(tc.str), func(t *testing.T) {
expect := tc.validPrint

if valid := asciiValidPrint([]byte(tc.str)); expect != valid {
t.Errorf("expected %t but got %t", expect, valid)
}
})
}
}

func limit(s string) string {
if len(s) > 17 {
return s[:17] + "..."
}
return s
}
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ require (
github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-isatty v0.0.20
github.com/nwidger/jsoncolor v0.3.0
github.com/segmentio/encoding v0.1.14
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
golang.org/x/sys v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
8 changes: 5 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/nwidger/jsoncolor v0.3.0 h1:VdTH8Dc0SJoq4pJ8pRxxFZW0/5Ng5akbN4YToCBJDSU=
github.com/nwidger/jsoncolor v0.3.0/go.mod h1:Cs34umxLbJvgBMnVNVqhji9BhoT/N/KinHqZptQ7cf4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -32,12 +34,12 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
23 changes: 6 additions & 17 deletions jsoncolor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"strconv"

"github.com/mattn/go-isatty"

"golang.org/x/term"
)

// Colors specifies colorization of JSON output. Each field
Expand Down Expand Up @@ -153,32 +151,23 @@ func IsColorTerminal(w io.Writer) bool {
return false
}

if !isTerminal(w) {
f, ok := w.(*os.File)
if !ok {
return false
}
fd := f.Fd()

if os.Getenv("TERM") == "dumb" {
if !isatty.IsTerminal(fd) {
return false
}

f, ok := w.(*os.File)
if !ok {
if os.Getenv("TERM") == "dumb" {
return false
}

if isatty.IsCygwinTerminal(f.Fd()) {
if isatty.IsCygwinTerminal(fd) {
return false
}

return true
}

// isTerminal returns true if w is a terminal.
func isTerminal(w io.Writer) bool {
switch v := w.(type) {
case *os.File:
return term.IsTerminal(int(v.Fd()))
default:
return false
}
}
6 changes: 2 additions & 4 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"unicode"
"unicode/utf16"
"unicode/utf8"

"github.com/segmentio/encoding/ascii"
)

// All spaces characters defined in the json specification.
Expand Down Expand Up @@ -398,7 +396,7 @@ func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
if n <= 1 {
return nil, b[len(b):], false, syntaxError(b, "missing '\"' at the end of a string value")
}
if bytes.IndexByte(b[1:n], '\\') < 0 && ascii.ValidPrint(b[1:n]) {
if bytes.IndexByte(b[1:n], '\\') < 0 && asciiValidPrint(b[1:n]) {
return b[:n], b[n:], false, nil
}

Expand Down Expand Up @@ -706,7 +704,7 @@ func hasLeadingZeroes(b []byte) bool {
}

func appendToLower(b, s []byte) []byte {
if ascii.Valid(s) { // fast path for ascii strings
if asciiValid(s) { // fast path for ascii strings
i := 0

for j := range s {
Expand Down

0 comments on commit ab3db80

Please sign in to comment.