Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ func bytesNativeFromTextual(buf []byte) (interface{}, []byte, error) {
// necessarily represent text. Therefore, Avro bytes are not
// encoded in UTF-16. Each \u is followed by 4 hexadecimal
// digits, the first and second of which must be 0.
v, err := parseUint64FromHexSlice(buf[i+3 : i+5])
v, err := parseUint64FromHexSlice(buf[i+1 : i+5])
if err != nil {
return nil, nil, fmt.Errorf("cannot decode textual bytes: %s", err)
}
if v > 255 {
return nil, nil, fmt.Errorf("cannot decode textual bytes: code point out of range: U+%04X", v)
}
i += 4 // absorb 4 characters: one 'u' and three of the digits
newBytes = append(newBytes, byte(v))
continue
Expand All @@ -136,6 +139,15 @@ func bytesNativeFromTextual(buf []byte) (interface{}, []byte, error) {
if b == '"' {
return newBytes, buf[i+1:], nil
}
if b >= utf8.RuneSelf {
r, width := utf8.DecodeRune(buf[i:])
if r == utf8.RuneError || r > 255 {
return nil, nil, fmt.Errorf("cannot decode textual bytes: invalid byte code point: %U", r)
}
newBytes = append(newBytes, byte(r))
i += width - 1
continue
}
newBytes = append(newBytes, b)
}
return nil, nil, fmt.Errorf("cannot decode textual bytes: expected final \"; found: %#U", buf[buflen-1])
Expand Down
47 changes: 47 additions & 0 deletions bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,50 @@ func TestStringCodecAcceptsBytes(t *testing.T) {
testTextEncodePass(t, schema, []byte("abcd"), []byte(`"abcd"`))
})
}

func TestBytesTextUnicodeCodePoints(t *testing.T) {
codec, err := NewCodec(`"bytes"`)
if err != nil {
t.Fatal(err)
}
for value := 0; value <= 255; value++ {
text, err := json.Marshal(string(rune(value)))
if err != nil {
t.Fatal(err)
}
decoded, remaining, err := codec.NativeFromTextual(append(text, ' '))
if err != nil {
t.Fatalf("code point %d: %v", value, err)
}
got := decoded.([]byte)
if len(got) != 1 || got[0] != byte(value) || string(remaining) != " " {
t.Errorf("code point %d: got %v with remaining %q", value, got, remaining)
}
}
}

func TestBytesTextRejectsOutOfRangeCodePoints(t *testing.T) {
for _, text := range []string{`"\u0100"`, `"\uffff"`, `"\uGGff"`, `"Ā"`, `"😂"`, "\"\xff\""} {
_, _, err := bytesNativeFromTextual([]byte(text))
if err == nil {
t.Errorf("expected error for %q", text)
}
}
}

func TestFixedTextUnicodeCodePoint(t *testing.T) {
codec, err := NewCodec(`{"type":"fixed","name":"single","size":1}`)
if err != nil {
t.Fatal(err)
}
for _, text := range []string{`"ÿ"`, `"\u00ff"`} {
decoded, rest, err := codec.NativeFromTextual([]byte(text))
if err != nil {
t.Fatal(err)
}
value := decoded.([]byte)
if len(value) != 1 || value[0] != 255 || len(rest) != 0 {
t.Fatalf("%s decoded to %v, remaining %q", text, value, rest)
}
}
}