func (s *Scanner) matchMobile(unigram []byte) {
	for _, mobile := range mobiles {
		if bytes.Equal(unigram, mobile.token) {
			s.mobile = true
		}
	}
}
match checks a unigram against the list of mobile tokens.
package agency
import (
	"bytes"
	"errors"
	"unicode"
	"unicode/utf8"
)
var errEOF = errors.New("eof")
type Scanner struct {
	c         rune
	buf       []byte
	buflen    int
	idx       int
	size      int
	prevstart int
	mobile    bool
	browsers  []*browser
	devices   []*device
	oses      []*aos
}
func NewScanner() *Scanner {
	return &Scanner{
		browsers: make([]*browser, maxRank),
		devices:  make([]*device, maxRank),
		oses:     make([]*aos, maxRank),
	}
}
func (s *Scanner) ScanBytes(b []byte) (*UserAgent, error) {
	var ua = new(UserAgent)
	s.buf = b
	s.buflen = len(b)
	s.reset()
	for {
		unigram, bigram, err := s.readNgrams()
		if err == errEOF {
			break
		} else if err != nil {
			return nil, err
		}
		if !s.mobile {
			s.matchMobile(unigram)
		}
		s.matchBrowser(unigram, bigram)
		s.matchDevice(unigram, bigram)
		s.matchOS(unigram, bigram)
	}
	for _, browser := range s.browsers {
		if browser != nil {
			ua.Browser.Type = browser.typ
			ua.Browser.Name = browser.name
			break
		}
	}
	for _, device := range s.devices {
		if device != nil {
			ua.Device.Type = device.typ
			break
		}
	}
	for _, os := range s.oses {
		if os != nil {
			ua.OS.Name = os.name
			ua.OS.Version = os.version
			break
		}
	}
	if s.mobile {
		if ua.Browser.Name == "Firefox" {
			ua.Browser.Type = "Mobile Browser"
			ua.Browser.Name = "Mobile Firefox"
		} else if ua.Browser.Name == "Safari" {
			ua.Browser.Type = "Mobile Browser"
			ua.Browser.Name = "Mobile Safari"
		} else if ua.Browser.Name == "Opera" {
			ua.Browser.Type = "Mobile Browser"
			ua.Browser.Name = "Opera Mobile"
		} else if ua.Browser.Name == "Yandex.Browser" {
			ua.Browser.Type = "Mobile Browser"
			ua.Browser.Name = "Yandex.Browser mobile"
		}
	}
	return ua, nil
}
func (s *Scanner) Scan(str string) (*UserAgent, error) {
	return s.ScanBytes([]byte(str))
}
func (s *Scanner) read() error {
	if s.idx >= s.buflen {
		return errEOF
	}
	b := s.buf[s.idx]
	if b < utf8.RuneSelf {
		s.c = rune(b)
		s.size = 1
	} else {
		s.c, s.size = utf8.DecodeRune(s.buf[s.idx:])
	}
	s.idx += s.size
	return nil
}
func (s *Scanner) unread() {
	s.idx -= s.size
	s.size = 0
}
func (s *Scanner) readNgrams() ([]byte, []byte, error) {
	var index int
	start := s.idx
	for {
		if err := s.read(); err == errEOF {
			break
		}
		if unicode.IsLetter(s.c) || unicode.IsDigit(s.c) || s.c == '-' || s.c == '.' {
			index++
		} else if index == 0 {
			start = s.idx
		} else {
			s.unread()
			break
		}
	}
	if s.idx == start {
		return nil, nil, errEOF
	}
	unigram := s.buf[start:s.idx]
	bigram := s.buf[s.prevstart:s.idx]
	s.prevstart = start
	return unigram, bigram, nil
}