107 lines
2.1 KiB
Go
107 lines
2.1 KiB
Go
// For more tutorials: https://blog.learngoprogramming.com
|
|
//
|
|
// Copyright © 2018 Inanc Gumus
|
|
// Learn Go Programming Course
|
|
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
|
//
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// this could be made faster.
|
|
// currently, it's 30-35% faster.
|
|
//
|
|
// so, what's different than the textreader?
|
|
//
|
|
// + creates the buffers specific to the input file/stdin size
|
|
// + manually parses the fields: instead of strings.Fields
|
|
// + gets the lines using scanner's Bytes() method: instead of Text()
|
|
// + uses a manual atoi
|
|
// +
|
|
|
|
func fastTextReader(r io.Reader) inputFunc {
|
|
return func() ([]result, error) {
|
|
// first: count the lines, so the parseText can create
|
|
// enough buffer.
|
|
var buf bytes.Buffer
|
|
l, err := countLines(io.TeeReader(r, &buf))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fastParseText(bufio.NewScanner(&buf), l)
|
|
}
|
|
}
|
|
|
|
func fastParseText(in *bufio.Scanner, nlines int) ([]result, error) {
|
|
// needs to know the number of total lines in the file
|
|
res := make([]result, 0, nlines)
|
|
|
|
for l := 0; in.Scan(); l++ {
|
|
r, err := fastParseFields(in.Bytes())
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("line %d: %v", l, err)
|
|
}
|
|
res = append(res, r)
|
|
}
|
|
|
|
return res, in.Err()
|
|
}
|
|
|
|
func fastParseFields(data []byte) (res result, err error) {
|
|
var field int
|
|
|
|
for i, last := 0, 0; i < len(data); i++ {
|
|
done := len(data) == i+1
|
|
|
|
if c := data[i]; c == ' ' || done {
|
|
if done {
|
|
i = len(data)
|
|
}
|
|
|
|
switch field {
|
|
case 0:
|
|
res.domain = string(data[last:i])
|
|
case 1:
|
|
res.page = string(data[last:i])
|
|
case 2:
|
|
res.visits, err = atoi(data[last:i])
|
|
case 3:
|
|
res.uniques, err = atoi(data[last:i])
|
|
}
|
|
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
last = i + 1
|
|
field++
|
|
}
|
|
}
|
|
|
|
if field != 4 {
|
|
return result{}, errors.New("wrong number of fields")
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func atoi(input []byte) (int, error) {
|
|
val := 0
|
|
for i := 0; i < len(input); i++ {
|
|
char := input[i]
|
|
if char < '0' || char > '9' {
|
|
return 0, errors.New("invalid number")
|
|
}
|
|
val = val*10 + int(char) - '0'
|
|
}
|
|
return val, nil
|
|
}
|