diff --git a/runtime/Go/antlr/v4/file_stream.go b/runtime/Go/antlr/v4/file_stream.go index 7f072e25ba..5f65f809be 100644 --- a/runtime/Go/antlr/v4/file_stream.go +++ b/runtime/Go/antlr/v4/file_stream.go @@ -5,8 +5,7 @@ package antlr import ( - "bytes" - "io" + "bufio" "os" ) @@ -14,38 +13,53 @@ import ( // when you construct the object. type FileStream struct { - *InputStream - + InputStream filename string } //goland:noinspection GoUnusedExportedFunction func NewFileStream(fileName string) (*FileStream, error) { - buf := bytes.NewBuffer(nil) - f, err := os.Open(fileName) if err != nil { return nil, err } + defer func(f *os.File) { errF := f.Close() if errF != nil { } }(f) - _, err = io.Copy(buf, f) + + reader := bufio.NewReader(f) + fInfo, err := f.Stat() if err != nil { return nil, err } - fs := new(FileStream) + fs := &FileStream{ + InputStream: InputStream{ + index: 0, + name: fileName, + }, + filename: fileName, + } - fs.filename = fileName - s := buf.String() - fs.InputStream = NewInputStream(s) + // Pre-build the buffer and read runes efficiently + // + fs.data = make([]rune, 0, fInfo.Size()) + for { + r, _, err := reader.ReadRune() + if err != nil { + break + } + fs.data = append(fs.data, r) + } + fs.size = len(fs.data) // Size in runes + // All done. + // return fs, nil - } func (f *FileStream) GetSourceName() string { diff --git a/runtime/Go/antlr/v4/input_stream.go b/runtime/Go/antlr/v4/input_stream.go index c02bbc76d3..b737fe85fb 100644 --- a/runtime/Go/antlr/v4/input_stream.go +++ b/runtime/Go/antlr/v4/input_stream.go @@ -4,6 +4,11 @@ package antlr +import ( + "bufio" + "io" +) + type InputStream struct { name string index int @@ -11,16 +16,43 @@ type InputStream struct { size int } -// NewInputStream creates a new input stream from the given string -func NewInputStream(data string) *InputStream { +// NewIoStream creates a new input stream from the given io.Reader reader. +// Note that the reader is read completely into memory and so it must actually +// have a stopping point - you cannot pass in a reader on an open-ended source such +// as a socket for instance. +func NewIoStream(reader io.Reader) *InputStream { - is := new(InputStream) + rReader := bufio.NewReader(reader) - is.name = "" - is.index = 0 - is.data = []rune(data) + is := &InputStream{ + name: "", + index: 0, + } + + // Pre-build the buffer and read runes reasonably efficiently given that + // we don't exactly know how big the input is. + // + is.data = make([]rune, 0, 512) + for { + r, _, err := rReader.ReadRune() + if err != nil { + break + } + is.data = append(is.data, r) + } is.size = len(is.data) // number of runes + return is +} + +// NewInputStream creates a new input stream from the given string +func NewInputStream(data string) *InputStream { + is := &InputStream{ + name: "", + index: 0, + data: []rune(data), // This is actually the most efficient way + } + is.size = len(is.data) // number of runes, but we could also use len(data), which is efficient too return is }