// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later package lowmemjson import ( "io" "git.lukeshu.com/go/lowmemjson/internal" ) type runeTypeScanner struct { // everything that is not "initialized by constructor" starts // out as the zero value. inner io.RuneScanner // initialized by constructor parser internal.Parser // initialized by constructor offset int64 initialized bool repeat bool rRune rune rSize int rType internal.RuneType rErr error } func (sc *runeTypeScanner) Reset() { sc.parser.Reset() if sc.repeat || (sc.rType == internal.RuneTypeEOF && sc.rSize > 0) { sc.repeat = false // re-figure the rType and rErr var err error sc.rType, err = sc.parser.HandleRune(sc.rRune) if err != nil { sc.rErr = &DecodeSyntaxError{ Offset: sc.offset - int64(sc.rSize), Err: err, } } else { sc.rErr = nil } // tell it to use that rType and rErr _ = sc.UnreadRune() // we set it up to always succeed } } // The returned error is a *ReadError, a *SyntaxError, or nil. // An EOF condition is represented as one of: // // end of value but not file: (_, >0, RuneTypeEOF, nil) // end of both value and file: (_, 0, RuneTypeEOF, nil) // end of file in middle of value: (_, 0, RuneTypeError, &DecodeSyntaxError{Offset: offset: Err: io.ErrUnexepctedEOF}) // end of file at start of value: (_, 0, RuneTypeError, &DecodeSyntaxError{Offset: offset: Err: io.EOF}) func (sc *runeTypeScanner) ReadRuneType() (rune, int, internal.RuneType, error) { switch { case sc.initialized && (sc.rType == internal.RuneTypeError || sc.rType == internal.RuneTypeEOF): // do nothing case sc.repeat: _, _, _ = sc.inner.ReadRune() default: sc.initialized = true again: var err error sc.rRune, sc.rSize, err = sc.inner.ReadRune() sc.offset += int64(sc.rSize) switch err { case nil: sc.rType, err = sc.parser.HandleRune(sc.rRune) if err != nil { sc.rErr = &DecodeSyntaxError{ Offset: sc.offset - int64(sc.rSize), Err: err, } } else { sc.rErr = nil } if sc.rType == internal.RuneTypeSpace { goto again } case io.EOF: sc.rType, err = sc.parser.HandleEOF() if err != nil { sc.rErr = &DecodeSyntaxError{ Offset: sc.offset, Err: err, } } else { sc.rErr = nil } default: sc.rType = 0 sc.rErr = &DecodeReadError{ Offset: sc.offset, Err: err, } } } sc.repeat = false return sc.rRune, sc.rSize, sc.rType, sc.rErr } // UnreadRune undoes a call to .ReadRune() or .ReadRuneType(). // // If the last call to .ReadRune() or .ReadRuneType() has already been // unread, or if that call returned a rune with size 0, then // ErrInvalidUnreadRune is returned. Otherwise, nil is returned. func (sc *runeTypeScanner) UnreadRune() error { if sc.repeat || sc.rSize == 0 { return ErrInvalidUnreadRune } sc.repeat = true _ = sc.inner.UnreadRune() return nil } func (sc *runeTypeScanner) InputOffset() int64 { ret := sc.offset if sc.repeat { ret -= int64(sc.rSize) } return ret } func (sc *runeTypeScanner) PushReadBarrier() { sc.parser.PushReadBarrier() } func (sc *runeTypeScanner) PopReadBarrier() { sc.parser.PopBarrier() if sc.repeat || (sc.rType == internal.RuneTypeEOF && sc.rSize > 0) { // re-figure the rType and rErr var err error sc.rType, err = sc.parser.HandleRune(sc.rRune) if err != nil { sc.rErr = &DecodeSyntaxError{ Offset: sc.offset - int64(sc.rSize), Err: err, } } else { sc.rErr = nil } // tell it to use that rType and rErr _ = sc.UnreadRune() // we set it up to always succeed } else if sc.rType == internal.RuneTypeEOF { // re-figure the rType and rErr var err error sc.rType, err = sc.parser.HandleEOF() if err != nil { sc.rErr = &DecodeSyntaxError{ Offset: sc.offset, Err: err, } } else { sc.rErr = nil } } }