Skip to content

Commit

Permalink
scan.Next is a pure function
Browse files Browse the repository at this point in the history
  • Loading branch information
awalterschulze committed Feb 4, 2025
1 parent 5f67c50 commit c5b9846
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 136 deletions.
110 changes: 55 additions & 55 deletions json/scan/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,128 +17,128 @@ package scan
type Kind byte

var kindMap = map[byte]Kind{
'{': objectOpenKind,
'}': objectCloseKind,
':': colonKind,
'[': arrayOpenKind,
']': arrayCloseKind,
',': commaKind,
'"': stringKind,
't': trueKind,
'f': falseKind,
'n': nullKind,
'-': numberKind,
'0': numberKind,
'1': numberKind,
'2': numberKind,
'3': numberKind,
'4': numberKind,
'5': numberKind,
'6': numberKind,
'7': numberKind,
'8': numberKind,
'9': numberKind,
'{': ObjectOpenKind,
'}': ObjectCloseKind,
':': ColonKind,
'[': ArrayOpenKind,
']': ArrayCloseKind,
',': CommaKind,
'"': StringKind,
't': TrueKind,
'f': FalseKind,
'n': NullKind,
'-': NumberKind,
'0': NumberKind,
'1': NumberKind,
'2': NumberKind,
'3': NumberKind,
'4': NumberKind,
'5': NumberKind,
'6': NumberKind,
'7': NumberKind,
'8': NumberKind,
'9': NumberKind,
}

func getKind(b byte) Kind {
k, ok := kindMap[b]
if ok {
return k
}
return unknownKind
return UnknownKind
}

const unknownKind = Kind(0)
const UnknownKind = Kind(0)

func (k Kind) isUnknown() bool {
return k == unknownKind
return k == UnknownKind
}

const objectOpenKind = Kind('{')
const ObjectOpenKind = Kind('{')

func (k Kind) isObjectOpen() bool {
return k == objectOpenKind
return k == ObjectOpenKind
}

const objectCloseKind = Kind('}')
const ObjectCloseKind = Kind('}')

func (k Kind) isObjectClose() bool {
return k == objectCloseKind
return k == ObjectCloseKind
}

const colonKind = Kind(':')
const ColonKind = Kind(':')

func (k Kind) isColon() bool {
return k == colonKind
return k == ColonKind
}

const arrayOpenKind = Kind('[')
const ArrayOpenKind = Kind('[')

func (k Kind) isArrayOpen() bool {
return k == arrayOpenKind
return k == ArrayOpenKind
}

const arrayCloseKind = Kind(']')
const ArrayCloseKind = Kind(']')

func (k Kind) isArrayClose() bool {
return k == arrayOpenKind
return k == ArrayCloseKind
}

const commaKind = Kind(',')
const CommaKind = Kind(',')

func (k Kind) isComma() bool {
return k == colonKind
return k == CommaKind
}

const stringKind = Kind('"')
const StringKind = Kind('"')

func (k Kind) isString() bool {
return k == stringKind
return k == StringKind
}

const numberKind = Kind('0')
const NumberKind = Kind('0')

func (k Kind) isNumber() bool {
return k == numberKind
return k == NumberKind
}

const trueKind = Kind('t')
const TrueKind = Kind('t')

func (k Kind) isTrue() bool {
return k == trueKind
return k == TrueKind
}

const falseKind = Kind('f')
const FalseKind = Kind('f')

func (k Kind) isFalse() bool {
return k == falseKind
return k == FalseKind
}

const nullKind = Kind('n')
const NullKind = Kind('n')

func (k Kind) isNull() bool {
return k == nullKind
return k == NullKind
}

func (k Kind) String() string {
switch k {
case unknownKind:
case UnknownKind:
return "unknown"
case falseKind:
case FalseKind:
return "false"
case trueKind:
case TrueKind:
return "true"
case numberKind:
case NumberKind:
return "number"
case stringKind:
case StringKind:
return "string"
case arrayOpenKind:
case ArrayOpenKind:
return "arrayOpen"
case arrayCloseKind:
case ArrayCloseKind:
return "arrayClose"
case objectOpenKind:
case ObjectOpenKind:
return "objectOpen"
case objectCloseKind:
case ObjectCloseKind:
return "objectClose"
}
return "other"
Expand Down
148 changes: 67 additions & 81 deletions json/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,128 +16,114 @@ package scan

import "io"

type Scanner interface {
Next() (Kind, []byte, error)
}

type scanner struct {
buf []byte
offset int
}

func NewScanner(buf []byte) Scanner {
return &scanner{
buf: buf,
offset: 0,
}
}

func (s *scanner) Next() (Kind, []byte, error) {
if err := s.skipSpace(); err != nil {
return unknownKind, nil, err
func Next(buf []byte, offset int) (Kind, int, error) {
var err error
offset, err = skipSpace(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
if s.offset == len(s.buf) {
return unknownKind, nil, io.EOF
if offset == len(buf) {
return UnknownKind, offset, io.EOF
}
c, err := s.look()
c, err := look(buf, offset)
if err != nil {
return unknownKind, nil, err
return UnknownKind, offset, err
}
kind := getKind(c)
start := s.offset
switch kind {
case objectOpenKind, objectCloseKind, arrayOpenKind, arrayCloseKind, colonKind, commaKind:
if err := s.incOffset(1); err != nil {
return unknownKind, nil, err
case ObjectOpenKind, ObjectCloseKind, ArrayOpenKind, ArrayCloseKind, ColonKind, CommaKind:
offset, err = incOffset(buf, offset, 1)
if err != nil {
return UnknownKind, offset, err
}
case stringKind:
if err := s.scanString(); err != nil {
return unknownKind, nil, err
case StringKind:
offset, err = scanString(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
case numberKind:
if err := s.scanNumber(); err != nil {
return unknownKind, nil, err
case NumberKind:
offset, err = scanNumber(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
case trueKind:
if err := s.scanTrue(); err != nil {
return unknownKind, nil, err
case TrueKind:
offset, err = scanTrue(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
case falseKind:
if err := s.scanFalse(); err != nil {
return unknownKind, nil, err
case FalseKind:
offset, err = scanFalse(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
case nullKind:
if err := s.scanNull(); err != nil {
return unknownKind, nil, err
case NullKind:
offset, err = scanNull(buf, offset)
if err != nil {
return UnknownKind, offset, err
}
}
end := s.offset
token := s.buf[start:end]
return kind, token, nil
return kind, offset, nil
}

func (s *scanner) scanNull() error {
n, err := Null(s.buf[s.offset:])
func scanNull(buf []byte, offset int) (int, error) {
n, err := Null(buf[offset:])
if err != nil {
return err
return 0, err
}
return s.incOffset(n)
return incOffset(buf, offset, n)
}

func (s *scanner) scanFalse() error {
n, err := False(s.buf[s.offset:])
func scanFalse(buf []byte, offset int) (int, error) {
n, err := False(buf[offset:])
if err != nil {
return err
return 0, err
}
return s.incOffset(n)
return incOffset(buf, offset, n)
}

func (s *scanner) scanTrue() error {
n, err := True(s.buf[s.offset:])
func scanTrue(buf []byte, offset int) (int, error) {
n, err := True(buf[offset:])
if err != nil {
return err
return 0, err
}
return s.incOffset(n)
return incOffset(buf, offset, n)
}

func (s *scanner) scanNumber() error {
n, err := Number(s.buf[s.offset:])
func scanNumber(buf []byte, offset int) (int, error) {
n, err := Number(buf[offset:])
if err != nil {
return err
return 0, err
}
return s.incOffset(n)
return incOffset(buf, offset, n)
}

func (s *scanner) scanString() error {
n, err := String(s.buf[s.offset:])
func scanString(buf []byte, offset int) (int, error) {
n, err := String(buf[offset:])
if err != nil {
return err
return 0, err
}
return s.incOffset(n)
return incOffset(buf, offset, n)
}

func (s *scanner) skipSpace() error {
if s.offset >= len(s.buf) {
return nil
}
n := Space(s.buf[s.offset:])
if err := s.incOffset(n); err != nil {
return err
func skipSpace(buf []byte, offset int) (int, error) {
if offset >= len(buf) {
return offset, nil
}
return nil
n := Space(buf[offset:])
return incOffset(buf, offset, n)
}

func (s *scanner) look() (byte, error) {
if s.offset < len(s.buf) {
return s.buf[s.offset], nil
func look(buf []byte, offset int) (byte, error) {
if offset < len(buf) {
return buf[offset], nil
}
return 0, io.ErrShortBuffer
}

func (s *scanner) incOffset(o int) error {
s.offset = s.offset + o
if s.offset > len(s.buf) {
return io.ErrShortBuffer
func incOffset(buf []byte, offset int, inc int) (int, error) {
offset = offset + inc
if offset > len(buf) {
return 0, io.ErrShortBuffer
}
return nil
return offset, nil
}
Loading

0 comments on commit c5b9846

Please sign in to comment.