diff options
Diffstat (limited to 'src')
287 files changed, 22766 insertions, 4835 deletions
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 518951fde..6c7f96483 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -417,7 +417,7 @@ adddynsym(Sym *s) return; if(s->dynimpname == nil) - diag("adddynsym: no dynamic name for %s", s->name, *(int32*)0); + diag("adddynsym: no dynamic name for %s", s->name); if(iself) { s->dynid = nelfsym++; diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 7faece81c..dc9edd6fd 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -95,5 +95,8 @@ process of using cgo. See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. Cgo does not yet work with gccgo. + +See "C? Go? Cgo!" for an introduction to using cgo: +http://blog.golang.org/2011/03/c-go-cgo.html */ package documentation diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 7ec4d8ccf..04d95f0b9 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -20,7 +20,6 @@ import ( "go/parser" "go/token" "os" - "runtime" "strconv" "strings" "unicode" @@ -91,9 +90,9 @@ NextLine: case 2: k = kf[1] switch kf[0] { - case runtime.GOOS: - case runtime.GOARCH: - case runtime.GOOS + "/" + runtime.GOARCH: + case goos: + case goarch: + case goos + "/" + goarch: default: continue NextLine } @@ -688,7 +687,7 @@ func (p *Package) gccName() (ret string) { // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". func (p *Package) gccMachine() []string { - switch runtime.GOARCH { + switch goarch { case "amd64": return []string{"-m64"} case "386": diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index be9c2bc4f..106698114 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "strings" ) @@ -122,6 +123,8 @@ var fset = token.NewFileSet() var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") +var goarch, goos string + func main() { flag.Usage = usage flag.Parse() @@ -162,13 +165,17 @@ func main() { goFiles := args[i:] - arch := os.Getenv("GOARCH") - if arch == "" { - fatalf("$GOARCH is not set") + goarch = runtime.GOARCH + if s := os.Getenv("GOARCH"); s != "" { + goarch = s + } + goos = runtime.GOOS + if s := os.Getenv("GOOS"); s != "" { + goos = s } - ptrSize := ptrSizeMap[arch] + ptrSize := ptrSizeMap[goarch] if ptrSize == 0 { - fatalf("unknown $GOARCH %q", arch) + fatalf("unknown $GOARCH %q", goarch) } // Clear locale variables so gcc emits English errors [sic]. diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index 5bc8d64aa..52038e71c 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -133,6 +133,9 @@ complexgen(Node *n, Node *res) dump("\ncomplexgen-n", n); dump("complexgen-res", res); } + + while(n->op == OCONVNOP) + n = n->left; // pick off float/complex opcodes switch(n->op) { diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index b5af4678c..e29cfff5b 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -67,4 +67,7 @@ static struct { % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME "nested func not allowed", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';' + "else must be followed by if or statement block" }; diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index a5e92bd4d..0c007f5f0 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -57,7 +57,7 @@ static void fixlbrace(int); %type <node> compound_stmt dotname embed expr complitexpr %type <node> expr_or_type %type <node> fndcl fnliteral -%type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt +%type <node> for_body for_header for_stmt if_header if_stmt else non_dcl_stmt %type <node> interfacedcl keyval labelname name %type <node> name_or_type non_expr_type %type <node> new_name dcl_name oexpr typedclname @@ -640,6 +640,7 @@ if_header: $$->ntest = $3; } +/* IF cond body (ELSE IF cond body)* (ELSE block)? */ if_stmt: LIF { @@ -652,9 +653,27 @@ if_stmt: } loop_body { + $3->nbody = $5; + } + else + { + popdcl(); $$ = $3; - $$->nbody = $5; - // no popdcl; maybe there's an LELSE + if($7 != N) + $$->nelse = list1($7); + } + +else: + { + $$ = N; + } +| LELSE if_stmt + { + $$ = $2; + } +| LELSE compound_stmt + { + $$ = $2; } switch_stmt: @@ -1474,19 +1493,6 @@ non_dcl_stmt: | switch_stmt | select_stmt | if_stmt - { - popdcl(); - $$ = $1; - } -| if_stmt LELSE stmt - { - if($3->op != OIF && $3->op != OBLOCK) - yyerror("missing { } after else"); - - popdcl(); - $$ = $1; - $$->nelse = list1($3); - } | labelname ':' { $1 = nod(OLABEL, $1, N); diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index d6fe6f65d..730b42671 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -125,7 +125,6 @@ static void outhist(Biobuf *b) { Hist *h; - int i, depth = 0; char *p, ds[] = {'c', ':', '/', 0}; for(h = hist; h != H; h = h->link) { @@ -162,14 +161,7 @@ outhist(Biobuf *b) outzfile(b, p); } } - if(h->offset > 0) { - //line directive - depth++; - } - } else if(depth > 0) { - for(i = 0; i < depth; i++) - zhist(b, h->line, h->offset); - depth = 0; + } zhist(b, h->line, h->offset); } diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 9448c3ffe..b450b9b0e 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -548,9 +548,13 @@ maptype(Type *key, Type *val) { Type *t; - - if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) { - if(key->etype == TFORW) { + if(key != nil) { + switch(key->etype) { + case TARRAY: + case TSTRUCT: + yyerror("invalid map key type %T", key); + break; + case TFORW: // map[key] used during definition of key. // postpone check until key is fully defined. // if there are multiple uses of map[key] @@ -559,8 +563,8 @@ maptype(Type *key, Type *val) // good enough. if(key->maplineno == 0) key->maplineno = lineno; - } else - yyerror("invalid map key type %T", key); + break; + } } t = typ(TMAP); t->down = key; diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go index baba53fa6..8c93425f3 100644 --- a/src/cmd/godoc/appinit.go +++ b/src/cmd/godoc/appinit.go @@ -4,8 +4,9 @@ // To run godoc under app engine, substitute main.go with // this file (appinit.go), provide a .zip file containing -// the file system to serve, and adjust the configuration -// parameters in appconfig.go accordingly. +// the file system to serve, the index file (or files) +// containing the pre-computed search index and adjust +// the configuration parameters in appconfig.go accordingly. // // The current app engine SDK may be based on an older Go // release version. To correct for version skew, copy newer @@ -17,7 +18,7 @@ // // The directory structure should look as follows: // -// godoc // directory containing the app engine app +// godoc-app // directory containing the app engine app // alt // alternative packages directory to // // correct for version skew // strings // never version of the strings package @@ -32,9 +33,8 @@ // // To run app the engine emulator locally: // -// dev_appserver.py -a 0 godoc +// dev_appserver.py -a 0 godoc-app // -// godoc is the top-level "goroot" directory. // The godoc home page is served at: <hostname>:8080 and localhost:8080. package main @@ -63,7 +63,7 @@ func init() { *goroot = path.Join("/", zipGoroot) // fsHttp paths are relative to '/' *indexEnabled = true *indexFiles = indexFilenames - *maxResults = 0 // save space for now + *maxResults = 100 // reduce latency by limiting the number of fulltext search results *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run) // read .zip file and set up file systems @@ -72,6 +72,7 @@ func init() { if err != nil { log.Fatalf("%s: %s\n", zipfile, err) } + // rc is never closed (app running forever) fs = NewZipFS(rc) fsHttp = NewHttpZipFS(rc, *goroot) diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index 602aa43a8..fb5f27596 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -18,7 +18,7 @@ import ( "io" "log" "os" - "regexp" + "exp/regexp" "sort" "strconv" "strings" @@ -129,7 +129,7 @@ func loadCodewalk(filename string) (*Codewalk, os.Error) { i = len(st.Src) } filename := st.Src[0:i] - data, err := fs.ReadFile(absolutePath(filename, *goroot)) + data, err := ReadFile(fs, absolutePath(filename, *goroot)) if err != nil { st.Err = err continue @@ -208,7 +208,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string // the usual godoc HTML wrapper. func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := absolutePath(f, *goroot) - data, err := fs.ReadFile(abspath) + data, err := ReadFile(fs, abspath) if err != nil { log.Print(err) serveError(w, r, f, err) diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 813527d28..3f0b8e458 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -137,5 +137,8 @@ one may run godoc as follows: godoc -http=:6060 -zip=go.zip -goroot=$HOME/go + +See "Godoc: documenting Go code" for how to write good comments for godoc: +http://blog.golang.org/2011/03/godoc-documenting-go-code.html */ package documentation diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go index a68c08592..011977af9 100644 --- a/src/cmd/godoc/filesystem.go +++ b/src/cmd/godoc/filesystem.go @@ -31,7 +31,16 @@ type FileSystem interface { Lstat(path string) (FileInfo, os.Error) Stat(path string) (FileInfo, os.Error) ReadDir(path string) ([]FileInfo, os.Error) - ReadFile(path string) ([]byte, os.Error) +} + +// ReadFile reads the file named by path from fs and returns the contents. +func ReadFile(fs FileSystem, path string) ([]byte, os.Error) { + rc, err := fs.Open(path) + if err != nil { + return nil, err + } + defer rc.Close() + return ioutil.ReadAll(rc) } // ---------------------------------------------------------------------------- @@ -98,7 +107,3 @@ func (osFS) ReadDir(path string) ([]FileInfo, os.Error) { } return l1, nil } - -func (osFS) ReadFile(path string) ([]byte, os.Error) { - return ioutil.ReadFile(path) -} diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index 78dde4166..91b746034 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -15,7 +15,7 @@ import ( "go/scanner" "go/token" "io" - "regexp" + "exp/regexp" "strconv" "template" ) diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index 6b646a1a6..3bf721bcc 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -19,7 +19,7 @@ import ( "os" "path" "path/filepath" - "regexp" + "exp/regexp" "runtime" "sort" "strings" @@ -149,7 +149,7 @@ func getPathFilter() func(string) bool { // readDirList reads a file containing a newline-separated list // of directory paths and returns the list of paths. func readDirList(filename string) ([]string, os.Error) { - contents, err := fs.ReadFile(filename) + contents, err := ReadFile(fs, filename) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func readTemplate(name string) *template.Template { // use underlying file system fs to read the template file // (cannot use template ParseFile functions directly) - data, err := fs.ReadFile(path) + data, err := ReadFile(fs, path) if err != nil { log.Fatal("readTemplate: ", err) } @@ -636,7 +636,7 @@ func extractString(src []byte, rx *regexp.Regexp) (s string) { func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { // get HTML body contents - src, err := fs.ReadFile(abspath) + src, err := ReadFile(fs, abspath) if err != nil { log.Printf("ReadFile: %s", err) serveError(w, r, relpath, err) @@ -685,7 +685,7 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { } func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { - src, err := fs.ReadFile(abspath) + src, err := ReadFile(fs, abspath) if err != nil { log.Printf("ReadFile: %s", err) serveError(w, r, relpath, err) @@ -815,6 +815,41 @@ type httpHandler struct { isPkg bool // true if this handler serves real package documentation (as opposed to command documentation) } +// fsReadDir implements ReadDir for the go/build package. +func fsReadDir(dir string) ([]*os.FileInfo, os.Error) { + fi, err := fs.ReadDir(dir) + if err != nil { + return nil, err + } + + // Convert []FileInfo to []*os.FileInfo. + osfi := make([]*os.FileInfo, len(fi)) + for i, f := range fi { + mode := uint32(S_IFREG) + if f.IsDirectory() { + mode = S_IFDIR + } + osfi[i] = &os.FileInfo{Name: f.Name(), Size: f.Size(), Mtime_ns: f.Mtime_ns(), Mode: mode} + } + return osfi, nil +} + +// fsReadFile implements ReadFile for the go/build package. +func fsReadFile(dir, name string) (path string, data []byte, err os.Error) { + path = filepath.Join(dir, name) + data, err = ReadFile(fs, path) + return +} + +func inList(name string, list []string) bool { + for _, l := range list { + if name == l { + return true + } + } + return false +} + // getPageInfo returns the PageInfo for a package directory abspath. If the // parameter genAST is set, an AST containing only the package exports is // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) @@ -824,11 +859,40 @@ type httpHandler struct { // PageInfo.Err is set to the respective error but the error is not logged. // func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { + var pkgFiles []string + + // If we're showing the default package, restrict to the ones + // that would be used when building the package on this + // system. This makes sure that if there are separate + // implementations for, say, Windows vs Unix, we don't + // jumble them all together. + if pkgname == "" { + // Note: Uses current binary's GOOS/GOARCH. + // To use different pair, such as if we allowed the user + // to choose, set ctxt.GOOS and ctxt.GOARCH before + // calling ctxt.ScanDir. + ctxt := build.DefaultContext + ctxt.ReadDir = fsReadDir + ctxt.ReadFile = fsReadFile + dir, err := ctxt.ScanDir(abspath) + if err == nil { + pkgFiles = append(dir.GoFiles, dir.CgoFiles...) + } + } + // filter function to select the desired .go files filter := func(d FileInfo) bool { + // Only Go files. + if !isPkgFile(d) { + return false + } // If we are looking at cmd documentation, only accept // the special fakePkgFile containing the documentation. - return isPkgFile(d) && (h.isPkg || d.Name() == fakePkgFile) + if !h.isPkg { + return d.Name() == fakePkgFile + } + // Also restrict file list to pkgFiles. + return pkgFiles == nil || inList(d.Name(), pkgFiles) } // get package ASTs @@ -1016,6 +1080,7 @@ type SearchResult struct { Alert string // error or warning message // identifier matches + Pak HitList // packages matching Query Hit *LookupResult // identifier matches of Query Alt *AltWords // alternative identifiers to look for @@ -1034,7 +1099,7 @@ func lookup(query string) (result SearchResult) { // identifier search var err os.Error - result.Hit, result.Alt, err = index.Lookup(query) + result.Pak, result.Hit, result.Alt, err = index.Lookup(query) if err != nil && *maxResults <= 0 { // ignore the error if full text search is enabled // since the query may be a valid regular expression diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index b99363491..2543f9216 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -48,7 +48,7 @@ import ( "io" "os" "path/filepath" - "regexp" + "exp/regexp" "sort" "strings" ) @@ -344,6 +344,8 @@ func reduce(h0 RunList) HitList { return h } +// filter returns a new HitList created by filtering +// all PakRuns from h that have a matching pakname. func (h HitList) filter(pakname string) HitList { var hh HitList for _, p := range h { @@ -831,34 +833,58 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool, throttle float64) *Ind return &Index{x.fset, suffixes, words, alts, x.snippets, x.stats} } -type FileIndex struct { +type fileIndex struct { Words map[string]*LookupResult Alts map[string]*AltWords Snippets []*Snippet + Fulltext bool } // Write writes the index x to w. func (x *Index) Write(w io.Writer) os.Error { + fulltext := false if x.suffixes != nil { - panic("no support for writing full text index yet") + fulltext = true } - fx := FileIndex{ + fx := fileIndex{ x.words, x.alts, x.snippets, + fulltext, } - return gob.NewEncoder(w).Encode(fx) + if err := gob.NewEncoder(w).Encode(fx); err != nil { + return err + } + if fulltext { + if err := x.fset.Write(w); err != nil { + return err + } + if err := x.suffixes.Write(w); err != nil { + return err + } + } + return nil } // Read reads the index from r into x; x must not be nil. func (x *Index) Read(r io.Reader) os.Error { - var fx FileIndex + var fx fileIndex if err := gob.NewDecoder(r).Decode(&fx); err != nil { return err } x.words = fx.Words x.alts = fx.Alts x.snippets = fx.Snippets + if fx.Fulltext { + x.fset = token.NewFileSet() + if err := x.fset.Read(r); err != nil { + return err + } + x.suffixes = new(suffixarray.Index) + if err := x.suffixes.Read(r); err != nil { + return err + } + } return nil } @@ -867,7 +893,7 @@ func (x *Index) Stats() Statistics { return x.stats } -func (x *Index) LookupWord(w string) (match *LookupResult, alt *AltWords) { +func (x *Index) lookupWord(w string) (match *LookupResult, alt *AltWords) { match = x.words[w] alt = x.alts[canonical(w)] // remove current spelling from alternatives @@ -891,9 +917,10 @@ func isIdentifier(s string) bool { } // For a given query, which is either a single identifier or a qualified -// identifier, Lookup returns a LookupResult, and a list of alternative -// spellings, if any. If the query syntax is wrong, an error is reported. -func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os.Error) { +// identifier, Lookup returns a list of packages, a LookupResult, and a +// list of alternative spellings, if any. Any and all results may be nil. +// If the query syntax is wrong, an error is reported. +func (x *Index) Lookup(query string) (paks HitList, match *LookupResult, alt *AltWords, err os.Error) { ss := strings.Split(query, ".") // check query syntax @@ -904,15 +931,23 @@ func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os } } + // handle simple and qualified identifiers switch len(ss) { case 1: - match, alt = x.LookupWord(ss[0]) + ident := ss[0] + match, alt = x.lookupWord(ident) + if match != nil { + // found a match - filter packages with same name + // for the list of packages called ident, if any + paks = match.Others.filter(ident) + } case 2: - pakname := ss[0] - match, alt = x.LookupWord(ss[1]) + pakname, ident := ss[0], ss[1] + match, alt = x.lookupWord(ident) if match != nil { // found a match - filter by package name + // (no paks - package names are not qualified) decls := match.Decls.filter(pakname) others := match.Others.filter(pakname) match = &LookupResult{decls, others} diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 74d3111ff..15d70c49b 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -40,7 +40,7 @@ import ( "os" "path" "path/filepath" - "regexp" + "exp/regexp" "runtime" "strings" "time" @@ -257,11 +257,6 @@ func main() { readTemplates() initHandlers() - if (*indexEnabled || *writeIndex) && *indexFiles != "" && *maxResults > 0 { - log.Println("warning: no support for full-text index yet (setting -maxresults to 0)") - *maxResults = 0 - } - if *writeIndex { // Write search index and exit. if *indexFiles == "" { diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go index cc1780a4b..a2920539f 100644 --- a/src/cmd/godoc/parser.go +++ b/src/cmd/godoc/parser.go @@ -18,7 +18,7 @@ import ( ) func parseFile(fset *token.FileSet, filename string, mode uint) (*ast.File, os.Error) { - src, err := fs.ReadFile(filename) + src, err := ReadFile(fs, filename) if err != nil { return nil, err } diff --git a/src/cmd/godoc/zip.go b/src/cmd/godoc/zip.go index 46d7112e5..86cd79b17 100644 --- a/src/cmd/godoc/zip.go +++ b/src/cmd/godoc/zip.go @@ -22,7 +22,6 @@ import ( "archive/zip" "fmt" "io" - "io/ioutil" "os" "path" "sort" @@ -153,14 +152,6 @@ func (fs *zipFS) ReadDir(abspath string) ([]FileInfo, os.Error) { return list, nil } -func (fs *zipFS) ReadFile(abspath string) ([]byte, os.Error) { - rc, err := fs.Open(abspath) - if err != nil { - return nil, err - } - return ioutil.ReadAll(rc) -} - func NewZipFS(rc *zip.ReadCloser) FileSystem { list := make(zipList, len(rc.File)) copy(list, rc.File) // sort a copy of rc.File diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile index d1f3ac605..b2725c572 100644 --- a/src/cmd/gofix/Makefile +++ b/src/cmd/gofix/Makefile @@ -12,6 +12,7 @@ GOFILES=\ httpfs.go\ httpheaders.go\ httpserver.go\ + imagenew.go\ main.go\ netdial.go\ netudpgroup.go\ diff --git a/src/cmd/gofix/imagenew.go b/src/cmd/gofix/imagenew.go new file mode 100644 index 000000000..0b3c0a307 --- /dev/null +++ b/src/cmd/gofix/imagenew.go @@ -0,0 +1,82 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +var imagenewFix = fix{ + "imagenew", + imagenew, + `Adapt image.NewXxx calls to pass an image.Rectangle instead of (w, h int). + +http://codereview.appspot.com/4964073 +`, +} + +func init() { + register(imagenewFix) +} + +var imagenewFuncs = map[string]bool{ + "NewRGBA": true, + "NewRGBA64": true, + "NewNRGBA": true, + "NewNRGBA64": true, + "NewAlpha": true, + "NewAlpha16": true, + "NewGray": true, + "NewGray16": true, +} + +func imagenew(f *ast.File) bool { + if !imports(f, "image") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok { + return + } + isNewFunc := false + for newFunc := range imagenewFuncs { + if len(call.Args) == 2 && isPkgDot(call.Fun, "image", newFunc) { + isNewFunc = true + break + } + } + if len(call.Args) == 3 && isPkgDot(call.Fun, "image", "NewPaletted") { + isNewFunc = true + } + if !isNewFunc { + return + } + // Replace image.NewXxx(w, h) with image.NewXxx(image.Rect(0, 0, w, h)). + rectArgs := []ast.Expr{ + &ast.BasicLit{Value: "0"}, + &ast.BasicLit{Value: "0"}, + } + rectArgs = append(rectArgs, call.Args[:2]...) + rect := []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: "image", + }, + Sel: &ast.Ident{ + Name: "Rect", + }, + }, + Args: rectArgs, + }, + } + call.Args = append(rect, call.Args[2:]...) + fixed = true + }) + return fixed +} diff --git a/src/cmd/gofix/imagenew_test.go b/src/cmd/gofix/imagenew_test.go new file mode 100644 index 000000000..3d40fea81 --- /dev/null +++ b/src/cmd/gofix/imagenew_test.go @@ -0,0 +1,53 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(imagenewTests) +} + +var imagenewTests = []testCase{ + { + Name: "imagenew.0", + In: `package main + +import ( + "image" +) + +func f() { + image.NewRGBA(1, 2) + image.NewRGBA64(1, 2) + image.NewNRGBA(1, 2) + image.NewNRGBA64(1, 2) + image.NewAlpha(1, 2) + image.NewAlpha16(1, 2) + image.NewGray(1, 2) + image.NewGray16(1, 2) + var m image.PalettedColorModel + image.NewPaletted(1, 2, m) +} +`, + Out: `package main + +import ( + "image" +) + +func f() { + image.NewRGBA(image.Rect(0, 0, 1, 2)) + image.NewRGBA64(image.Rect(0, 0, 1, 2)) + image.NewNRGBA(image.Rect(0, 0, 1, 2)) + image.NewNRGBA64(image.Rect(0, 0, 1, 2)) + image.NewAlpha(image.Rect(0, 0, 1, 2)) + image.NewAlpha16(image.Rect(0, 0, 1, 2)) + image.NewGray(image.Rect(0, 0, 1, 2)) + image.NewGray16(image.Rect(0, 0, 1, 2)) + var m image.PalettedColorModel + image.NewPaletted(image.Rect(0, 0, 1, 2), m) +} +`, + }, +} diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go index e7e7013c5..e0709fc8b 100644 --- a/src/cmd/gofix/main.go +++ b/src/cmd/gofix/main.go @@ -198,31 +198,17 @@ func report(err os.Error) { } func walkDir(path string) { - v := make(fileVisitor) - go func() { - filepath.Walk(path, v, v) - close(v) - }() - for err := range v { - if err != nil { - report(err) - } - } -} - -type fileVisitor chan os.Error - -func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool { - return true + filepath.Walk(path, visitFile) } -func (v fileVisitor) VisitFile(path string, f *os.FileInfo) { - if isGoFile(f) { - v <- nil // synchronize error handler - if err := processFile(path, false); err != nil { - v <- err - } +func visitFile(path string, f *os.FileInfo, err os.Error) os.Error { + if err == nil && isGoFile(f) { + err = processFile(path, false) + } + if err != nil { + report(err) } + return nil } func isGoFile(f *os.FileInfo) bool { diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index fca42b76b..3a20c21e0 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -53,6 +53,12 @@ In the pattern, single-character lowercase identifiers serve as wildcards matching arbitrary sub-expressions; those expressions will be substituted for the same identifiers in the replacement. +When gofmt reads from standard input, it accepts either a full Go program +or a program fragment. A program fragment must be a syntactically +valid declaration list, statement list, or expression. When formatting +such a fragment, gofmt preserves leading indentation as well as leading +and trailing spaces, so that individual sections of a Go program can be +formatted by piping them through gofmt. Examples diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 975ae6ac6..1c0efb6db 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -86,7 +86,7 @@ func isGoFile(f *os.FileInfo) bool { } // If in == nil, the source is the contents of the file with the given filename. -func processFile(filename string, in io.Reader, out io.Writer) os.Error { +func processFile(filename string, in io.Reader, out io.Writer, stdin bool) os.Error { if in == nil { f, err := os.Open(filename) if err != nil { @@ -101,7 +101,7 @@ func processFile(filename string, in io.Reader, out io.Writer) os.Error { return err } - file, err := parser.ParseFile(fset, filename, src, parserMode) + file, adjust, err := parse(filename, src, stdin) if err != nil { return err } @@ -119,7 +119,7 @@ func processFile(filename string, in io.Reader, out io.Writer) os.Error { if err != nil { return err } - res := buf.Bytes() + res := adjust(src, buf.Bytes()) if !bytes.Equal(src, res) { // formatting has changed @@ -149,32 +149,18 @@ func processFile(filename string, in io.Reader, out io.Writer) os.Error { return err } -type fileVisitor chan os.Error - -func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool { - return true -} - -func (v fileVisitor) VisitFile(path string, f *os.FileInfo) { - if isGoFile(f) { - v <- nil // synchronize error handler - if err := processFile(path, nil, os.Stdout); err != nil { - v <- err - } +func visitFile(path string, f *os.FileInfo, err os.Error) os.Error { + if err == nil && isGoFile(f) { + err = processFile(path, nil, os.Stdout, false) } + if err != nil { + report(err) + } + return nil } func walkDir(path string) { - v := make(fileVisitor) - go func() { - filepath.Walk(path, v, v) - close(v) - }() - for err := range v { - if err != nil { - report(err) - } - } + filepath.Walk(path, visitFile) } func main() { @@ -211,7 +197,7 @@ func gofmtMain() { initRewrite() if flag.NArg() == 0 { - if err := processFile("<standard input>", os.Stdin, os.Stdout); err != nil { + if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil { report(err) } return @@ -223,7 +209,7 @@ func gofmtMain() { case err != nil: report(err) case dir.IsRegular(): - if err := processFile(path, nil, os.Stdout); err != nil { + if err := processFile(path, nil, os.Stdout, false); err != nil { report(err) } case dir.IsDirectory(): @@ -259,3 +245,109 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) { return } + +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, os.Error) { + // Try as whole source file. + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { return src } + return file, adjust, nil + } + // If the error is that the source file didn't begin with a + // package line and this is standard input, fall through to + // try as a source fragment. Stop and return on any other error. + if !stdin || !strings.Contains(err.String(), "expected 'package'") { + return nil, nil, err + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that the line numbers + // in psrc match the ones in src. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { + // Remove the package clause. + // Gofmt has turned the ; into a \n. + src = src[len("package p\n"):] + return matchSpace(orig, src) + } + return file, adjust, nil + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.String(), "expected declaration") { + return nil, nil, err + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + src = src[len("package p\n\nfunc _() {"):] + src = src[:len(src)-len("}\n")] + // Gofmt has also indented the function body one level. + // Remove that indent. + src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) + return matchSpace(orig, src) + } + return file, adjust, nil + } + + // Failed, and out of options. + return nil, nil, err +} + +func cutSpace(b []byte) (before, middle, after []byte) { + i := 0 + for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { + i++ + } + j := len(b) + for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { + j-- + } + return b[:i], b[i:j], b[j:] +} + +// matchSpace reformats src to use the same space context as orig. +// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. +// 2) matchSpace copies the indentation of the first non-blank line in orig +// to every non-blank line in src. +// 3) matchSpace copies the trailing space from orig and uses it in place +// of src's trailing space. +func matchSpace(orig []byte, src []byte) []byte { + before, _, after := cutSpace(orig) + i := bytes.LastIndex(before, []byte{'\n'}) + before, indent := before[:i+1], before[i+1:] + + _, src, _ = cutSpace(src) + + var b bytes.Buffer + b.Write(before) + for len(src) > 0 { + line := src + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, src = line[:i+1], line[i+1:] + } else { + src = nil + } + if len(line) > 0 && line[0] != '\n' { // not blank + b.Write(indent) + } + b.Write(line) + } + b.Write(after) + return b.Bytes() +} diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go index 2e35ce9a4..87b02dad7 100644 --- a/src/cmd/gofmt/gofmt_test.go +++ b/src/cmd/gofmt/gofmt_test.go @@ -12,13 +12,11 @@ import ( "testing" ) -func runTest(t *testing.T, dirname, in, out, flags string) { - in = filepath.Join(dirname, in) - out = filepath.Join(dirname, out) - +func runTest(t *testing.T, in, out, flags string) { // process flags *simplifyAST = false *rewriteRule = "" + stdin := false for _, flag := range strings.Split(flags, " ") { elts := strings.SplitN(flag, "=", 2) name := elts[0] @@ -33,6 +31,9 @@ func runTest(t *testing.T, dirname, in, out, flags string) { *rewriteRule = value case "-s": *simplifyAST = true + case "-stdin": + // fake flag - pretend input is from stdin + stdin = true default: t.Errorf("unrecognized flag name: %s", name) } @@ -43,7 +44,7 @@ func runTest(t *testing.T, dirname, in, out, flags string) { initRewrite() var buf bytes.Buffer - err := processFile(in, nil, &buf) + err := processFile(in, nil, &buf, stdin) if err != nil { t.Error(err) return @@ -57,23 +58,44 @@ func runTest(t *testing.T, dirname, in, out, flags string) { if got := buf.Bytes(); bytes.Compare(got, expected) != 0 { t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) + d, err := diff(expected, got) + if err == nil { + t.Errorf("%s", d) + } ioutil.WriteFile(in+".gofmt", got, 0666) } } // TODO(gri) Add more test cases! var tests = []struct { - dirname, in, out, flags string + in, flags string }{ - {".", "gofmt.go", "gofmt.go", ""}, - {".", "gofmt_test.go", "gofmt_test.go", ""}, - {"testdata", "composites.input", "composites.golden", "-s"}, - {"testdata", "rewrite1.input", "rewrite1.golden", "-r=Foo->Bar"}, - {"testdata", "rewrite2.input", "rewrite2.golden", "-r=int->bool"}, + {"gofmt.go", ""}, + {"gofmt_test.go", ""}, + {"testdata/composites.input", "-s"}, + {"testdata/old.input", ""}, + {"testdata/rewrite1.input", "-r=Foo->Bar"}, + {"testdata/rewrite2.input", "-r=int->bool"}, + {"testdata/stdin*.input", "-stdin"}, } func TestRewrite(t *testing.T) { for _, test := range tests { - runTest(t, test.dirname, test.in, test.out, test.flags) + match, err := filepath.Glob(test.in) + if err != nil { + t.Error(err) + continue + } + for _, in := range match { + out := in + if strings.HasSuffix(in, ".input") { + out = in[:len(in)-len(".input")] + ".golden" + } + runTest(t, in, out, test.flags) + if in != out { + // Check idempotence. + runTest(t, out, out, test.flags) + } + } } } diff --git a/src/cmd/gofmt/testdata/old.golden b/src/cmd/gofmt/testdata/old.golden new file mode 100644 index 000000000..95a0b72a0 --- /dev/null +++ b/src/cmd/gofmt/testdata/old.golden @@ -0,0 +1,9 @@ +package P + +func f() { + if x { + y + } else { + z + } +} diff --git a/src/cmd/gofmt/testdata/old.input b/src/cmd/gofmt/testdata/old.input new file mode 100644 index 000000000..e24eed215 --- /dev/null +++ b/src/cmd/gofmt/testdata/old.input @@ -0,0 +1,8 @@ +package P + +func f() { + if x { + y + } else + z +} diff --git a/src/cmd/gofmt/testdata/stdin1.golden b/src/cmd/gofmt/testdata/stdin1.golden new file mode 100644 index 000000000..ff8b0b7ab --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.golden @@ -0,0 +1,3 @@ + if x { + y + } diff --git a/src/cmd/gofmt/testdata/stdin1.golden.gofmt b/src/cmd/gofmt/testdata/stdin1.golden.gofmt new file mode 100644 index 000000000..1f888877d --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.golden.gofmt @@ -0,0 +1,3 @@ + if x { + y +} diff --git a/src/cmd/gofmt/testdata/stdin1.input b/src/cmd/gofmt/testdata/stdin1.input new file mode 100644 index 000000000..ff8b0b7ab --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.input @@ -0,0 +1,3 @@ + if x { + y + } diff --git a/src/cmd/gofmt/testdata/stdin1.input.gofmt b/src/cmd/gofmt/testdata/stdin1.input.gofmt new file mode 100644 index 000000000..1f888877d --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.input.gofmt @@ -0,0 +1,3 @@ + if x { + y +} diff --git a/src/cmd/gofmt/testdata/stdin2.golden b/src/cmd/gofmt/testdata/stdin2.golden new file mode 100644 index 000000000..7eb1b54fe --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.golden @@ -0,0 +1,11 @@ + + +var x int + +func f() { + y := z + /* this is a comment */ + // this is a comment too +} + + diff --git a/src/cmd/gofmt/testdata/stdin2.golden.gofmt b/src/cmd/gofmt/testdata/stdin2.golden.gofmt new file mode 100644 index 000000000..85e800300 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.golden.gofmt @@ -0,0 +1,10 @@ + + + +var x int + +func f() { + y := z +} + + diff --git a/src/cmd/gofmt/testdata/stdin2.input b/src/cmd/gofmt/testdata/stdin2.input new file mode 100644 index 000000000..99defd2d1 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.input @@ -0,0 +1,11 @@ + + +var x int + + +func f() { y := z + /* this is a comment */ + // this is a comment too +} + + diff --git a/src/cmd/gofmt/testdata/stdin2.input.gofmt b/src/cmd/gofmt/testdata/stdin2.input.gofmt new file mode 100644 index 000000000..7eb1b54fe --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.input.gofmt @@ -0,0 +1,11 @@ + + +var x int + +func f() { + y := z + /* this is a comment */ + // this is a comment too +} + + diff --git a/src/cmd/gofmt/testdata/stdin3.golden b/src/cmd/gofmt/testdata/stdin3.golden new file mode 100644 index 000000000..1bf2f5a48 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.golden @@ -0,0 +1,6 @@ + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { + s += i + } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin3.golden.gofmt b/src/cmd/gofmt/testdata/stdin3.golden.gofmt new file mode 100644 index 000000000..b4d1d4663 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.golden.gofmt @@ -0,0 +1,7 @@ + + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { + s += i + } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin3.input b/src/cmd/gofmt/testdata/stdin3.input new file mode 100644 index 000000000..d963bd0d2 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.input @@ -0,0 +1,4 @@ + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { s += i } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin3.input.gofmt b/src/cmd/gofmt/testdata/stdin3.input.gofmt new file mode 100644 index 000000000..b4d1d4663 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.input.gofmt @@ -0,0 +1,7 @@ + + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { + s += i + } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin4.golden b/src/cmd/gofmt/testdata/stdin4.golden new file mode 100644 index 000000000..5f7343551 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.golden @@ -0,0 +1,3 @@ + // comment + + i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.golden.gofmt b/src/cmd/gofmt/testdata/stdin4.golden.gofmt new file mode 100644 index 000000000..5f7343551 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.golden.gofmt @@ -0,0 +1,3 @@ + // comment + + i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.input b/src/cmd/gofmt/testdata/stdin4.input new file mode 100644 index 000000000..f02a54fb1 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.input @@ -0,0 +1,3 @@ + // comment + + i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.input.gofmt b/src/cmd/gofmt/testdata/stdin4.input.gofmt new file mode 100644 index 000000000..5f7343551 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.input.gofmt @@ -0,0 +1,3 @@ + // comment + + i := 0 diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index 23b26e383..478266357 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -13,7 +13,7 @@ import ( "go/token" "io/ioutil" "os" - "path/filepath" + "path/filepath" // use for file system paths "regexp" "runtime" "strings" @@ -190,7 +190,7 @@ func install(pkg, parent string) { }() // Don't allow trailing '/' - if _, f := filepath.Split(pkg); f == "" { + if strings.HasSuffix(pkg, "/") { errorf("%s should not have trailing '/'\n", pkg) return } @@ -225,16 +225,17 @@ func install(pkg, parent string) { terrorf(tree, "%s: %v\n", pkg, err) return } - dir := filepath.Join(tree.SrcDir(), pkg) + dir := filepath.Join(tree.SrcDir(), filepath.FromSlash(pkg)) // Install prerequisites. - dirInfo, err := build.ScanDir(dir, parent == "") + dirInfo, err := build.ScanDir(dir) if err != nil { terrorf(tree, "%s: %v\n", pkg, err) return } - if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 { - terrorf(tree, "%s: package has no files\n", pkg) + // We reserve package main to identify commands. + if parent != "" && dirInfo.Package == "main" { + terrorf(tree, "%s: found only package main in %s; cannot import", pkg, dir) return } for _, p := range dirInfo.Imports { diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index 38a70ddfd..7f41a913f 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -10,7 +10,7 @@ import ( "bytes" "go/build" "os" - "path/filepath" + "path" // use for import paths "strings" "template" ) @@ -44,10 +44,10 @@ func makeMakefile(dir, pkg string, tree *build.Tree, isCmd bool) ([]byte, os.Err targDir := tree.PkgDir() if isCmd { // use the last part of the package name for targ - _, targ = filepath.Split(pkg) + _, targ = path.Split(pkg) targDir = tree.BinDir() } - dirInfo, err := build.ScanDir(dir, isCmd) + dirInfo, err := build.ScanDir(dir) if err != nil { return nil, err } diff --git a/src/cmd/gotest/gotest.go b/src/cmd/gotest/gotest.go index 8e3a42232..88c746c1b 100644 --- a/src/cmd/gotest/gotest.go +++ b/src/cmd/gotest/gotest.go @@ -165,7 +165,7 @@ func setEnvironment() { func getTestFileNames() { names := fileNames if len(names) == 0 { - info, err := build.ScanDir(".", true) + info, err := build.ScanDir(".") if err != nil { Fatalf("scanning directory: %v", err) } diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go index 98d3d5c17..9aa97e316 100644 --- a/src/cmd/govet/govet.go +++ b/src/cmd/govet/govet.go @@ -101,34 +101,20 @@ func doFile(name string, reader io.Reader) { file.checkFile(name, parsedFile) } -// Visitor for filepath.Walk - trivial. Just calls doFile on each file. -// TODO: if govet becomes richer, might want to process -// a directory (package) at a time. -type V struct{} - -func (v V) VisitDir(path string, f *os.FileInfo) bool { - return true -} - -func (v V) VisitFile(path string, f *os.FileInfo) { - if strings.HasSuffix(path, ".go") { +func visit(path string, f *os.FileInfo, err os.Error) os.Error { + if err != nil { + errorf("walk error: %s", err) + return nil + } + if f.IsRegular() && strings.HasSuffix(path, ".go") { doFile(path, nil) } + return nil } // walkDir recursively walks the tree looking for .go files. func walkDir(root string) { - errors := make(chan os.Error) - done := make(chan bool) - go func() { - for e := range errors { - errorf("walk error: %s", e) - } - done <- true - }() - filepath.Walk(root, V{}, errors) - close(errors) - <-done + filepath.Walk(root, visit) } // error formats the error to standard error, adding program diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index d8ca27ace..77536018a 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -1578,13 +1578,16 @@ addhistfile(char *zentry) histfile[histfilesize++] = "<eof>"; fname = decodez(zentry); +// print("addhistfile %d: %s\n", histfilesize, fname); if (fname == 0) return -1; + // Don't fill with duplicates (check only top one). if (strcmp(fname, histfile[histfilesize-1]) == 0) { free(fname); return histfilesize - 1; } + histfile[histfilesize++] = fname; return histfilesize - 1; } @@ -1608,11 +1611,13 @@ finddebugruntimepath(void) } // Go's runtime C sources are sane, and Go sources nest only 1 level, -// so 16 should be plenty. +// so a handful would be plenty, if it weren't for the fact that line +// directives can push an unlimited number of them. static struct { int file; vlong line; -} includestack[16]; +} *includestack; +static int includestacksize; static int includetop; static vlong absline; @@ -1629,17 +1634,15 @@ static Linehist *linehist; static void checknesting(void) { - int i; - if (includetop < 0) { diag("dwarf: corrupt z stack"); errorexit(); } - if (includetop >= nelem(includestack)) { - diag("dwarf: nesting too deep"); - for (i = 0; i < nelem(includestack); i++) - diag("\t%s", histfile[includestack[i].file]); - errorexit(); + if (includetop >= includestacksize) { + includestacksize += 1; + includestacksize <<= 2; +// print("checknesting: growing to %d\n", includestacksize); + includestack = realloc(includestack, includestacksize * sizeof *includestack); } } @@ -1669,6 +1672,7 @@ inithist(Auto *a) // Clear the history. clearhistfile(); includetop = 0; + checknesting(); includestack[includetop].file = 0; includestack[includetop].line = -1; absline = 0; @@ -1682,10 +1686,10 @@ inithist(Auto *a) for (; a; a = a->link) { if (a->type == D_FILE) { // 'z' int f = addhistfile(a->asym->name); - if (f < 0) { // pop file + if (f < 0) { // pop file includetop--; checknesting(); - } else if(f != includestack[includetop].file) { // pushed a new file + } else { // pushed a file (potentially same) includestack[includetop].line += a->aoffset - absline; includetop++; checknesting(); diff --git a/src/pkg/Makefile b/src/pkg/Makefile index c7e65c029..85c5031e5 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -33,6 +33,7 @@ DIRS=\ crypto\ crypto/aes\ crypto/blowfish\ + crypto/bcrypt\ crypto/cast5\ crypto/cipher\ crypto/des\ diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go index 528858765..67355086a 100644 --- a/src/pkg/archive/tar/common.go +++ b/src/pkg/archive/tar/common.go @@ -15,36 +15,37 @@ const ( blockSize = 512 // Types - TypeReg = '0' - TypeRegA = '\x00' - TypeLink = '1' - TypeSymlink = '2' - TypeChar = '3' - TypeBlock = '4' - TypeDir = '5' - TypeFifo = '6' - TypeCont = '7' - TypeXHeader = 'x' - TypeXGlobalHeader = 'g' + TypeReg = '0' // regular file. + TypeRegA = '\x00' // regular file. + TypeLink = '1' // hard link. + TypeSymlink = '2' // symbolic link. + TypeChar = '3' // character device node. + TypeBlock = '4' // block device node. + TypeDir = '5' // directory. + TypeFifo = '6' // fifo node. + TypeCont = '7' // reserved. + TypeXHeader = 'x' // extended header. + TypeXGlobalHeader = 'g' // global extended header. ) // A Header represents a single header in a tar archive. // Some fields may not be populated. type Header struct { - Name string - Mode int64 - Uid int - Gid int - Size int64 - Mtime int64 - Typeflag byte - Linkname string - Uname string - Gname string - Devmajor int64 - Devminor int64 - Atime int64 - Ctime int64 + Name string // name of header file entry. + Mode int64 // permission and mode bits. + Uid int // user id of owner. + Gid int // group id of owner. + Size int64 // length in bytes. + Mtime int64 // modified time; seconds since epoch. + Typeflag byte // type of header entry. + Linkname string // target name of link. + Uname string // user name of owner. + Gname string // group name of owner. + Devmajor int64 // major number of character or block device. + Devminor int64 // minor number of character or block device. + Atime int64 // access time; seconds since epoch. + Ctime int64 // status change time; seconds since epoch. + } var zeroBlock = make([]byte, blockSize) diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go index 9f48f7bdd..1c529bdb3 100644 --- a/src/pkg/asn1/asn1_test.go +++ b/src/pkg/asn1/asn1_test.go @@ -206,10 +206,10 @@ type timeTest struct { } var utcTestData = []timeTest{ - {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, -7 * 60 * 60, ""}}, - {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, 7*60*60 + 30*60, ""}}, - {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, 0, "UTC"}}, - {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, 0, "UTC"}}, + {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, + {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, + {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}}, + {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}}, {"a10506234540Z", false, nil}, {"91a506234540Z", false, nil}, {"9105a6234540Z", false, nil}, @@ -235,10 +235,10 @@ func TestUTCTime(t *testing.T) { } var generalizedTimeTestData = []timeTest{ - {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 0, "UTC"}}, + {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}}, {"20100102030405", false, nil}, - {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 6*60*60 + 7*60, ""}}, - {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, -6*60*60 - 7*60, ""}}, + {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}}, + {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}}, } func TestGeneralizedTime(t *testing.T) { @@ -475,7 +475,7 @@ var derEncodedSelfSignedCert = Certificate{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, + Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}}, Subject: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 5119fce94..ea6bf5ec2 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -572,13 +572,18 @@ func Runes(s []byte) []int { // non-overlapping instances of old replaced by new. // If n < 0, there is no limit on the number of replacements. func Replace(s, old, new []byte, n int) []byte { - if n == 0 { - return s // avoid allocation - } - // Compute number of replacements. - if m := Count(s, old); m == 0 { - return s // avoid allocation - } else if n <= 0 || m < n { + m := 0 + if n != 0 { + // Compute number of replacements. + m = Count(s, old) + } + if m == 0 { + // Nothing to do. Just copy. + t := make([]byte, len(s)) + copy(t, s) + return t + } + if n < 0 || m < n { n = m } diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 9444358a8..1679279d3 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -829,9 +829,15 @@ var ReplaceTests = []ReplaceTest{ func TestReplace(t *testing.T) { for _, tt := range ReplaceTests { - if s := string(Replace([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)); s != tt.out { + in := append([]byte(tt.in), []byte("<spare>")...) + in = in[:len(tt.in)] + out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n) + if s := string(out); s != tt.out { t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out) } + if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { + t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n) + } } } diff --git a/src/pkg/crypto/bcrypt/Makefile b/src/pkg/crypto/bcrypt/Makefile new file mode 100644 index 000000000..3c83d9c8a --- /dev/null +++ b/src/pkg/crypto/bcrypt/Makefile @@ -0,0 +1,12 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=crypto/bcrypt +GOFILES=\ + base64.go \ + bcrypt.go + +include ../../../Make.pkg diff --git a/src/pkg/crypto/bcrypt/base64.go b/src/pkg/crypto/bcrypt/base64.go new file mode 100644 index 000000000..ed6cea70c --- /dev/null +++ b/src/pkg/crypto/bcrypt/base64.go @@ -0,0 +1,38 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bcrypt + +import ( + "encoding/base64" + "os" +) + +const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +var bcEncoding = base64.NewEncoding(alphabet) + +func base64Encode(src []byte) []byte { + n := bcEncoding.EncodedLen(len(src)) + dst := make([]byte, n) + bcEncoding.Encode(dst, src) + for dst[n-1] == '=' { + n-- + } + return dst[:n] +} + +func base64Decode(src []byte) ([]byte, os.Error) { + numOfEquals := 4 - (len(src) % 4) + for i := 0; i < numOfEquals; i++ { + src = append(src, '=') + } + + dst := make([]byte, bcEncoding.DecodedLen(len(src))) + n, err := bcEncoding.Decode(dst, src) + if err != nil { + return nil, err + } + return dst[:n], nil +} diff --git a/src/pkg/crypto/bcrypt/bcrypt.go b/src/pkg/crypto/bcrypt/bcrypt.go new file mode 100644 index 000000000..1e8ccfac1 --- /dev/null +++ b/src/pkg/crypto/bcrypt/bcrypt.go @@ -0,0 +1,282 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bcrypt implements Provos and Mazières's bcrypt adapative hashing +// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf +package bcrypt + +// The code is a port of Provos and Mazières's C implementation. +import ( + "crypto/blowfish" + "crypto/rand" + "crypto/subtle" + "fmt" + "io" + "os" + "strconv" +) + +const ( + MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword + MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword + DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword +) + +// The error returned from CompareHashAndPassword when a password and hash do +// not match. +var MismatchedHashAndPasswordError = os.NewError("crypto/bcrypt: hashedPassword is not the hash of the given password") + +// The error returned from CompareHashAndPassword when a hash is too short to +// be a bcrypt hash. +var HashTooShortError = os.NewError("crypto/bcrypt: hashedSecret too short to be a bcrypted password") + +// The error returned from CompareHashAndPassword when a hash was created with +// a bcrypt algorithm newer than this implementation. +type HashVersionTooNewError byte + +func (hv HashVersionTooNewError) String() string { + return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion) +} + +// The error returned from CompareHashAndPassword when a hash starts with something other than '$' +type InvalidHashPrefixError byte + +func (ih InvalidHashPrefixError) String() string { + return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih)) +} + +type InvalidCostError int + +func (ic InvalidCostError) String() string { + return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) +} + +const ( + majorVersion = '2' + minorVersion = 'a' + maxSaltSize = 16 + maxCryptedHashSize = 23 + encodedSaltSize = 22 + encodedHashSize = 31 + minHashSize = 59 +) + +// magicCipherData is an IV for the 64 Blowfish encryption calls in +// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes. +var magicCipherData = []byte{ + 0x4f, 0x72, 0x70, 0x68, + 0x65, 0x61, 0x6e, 0x42, + 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x53, + 0x63, 0x72, 0x79, 0x44, + 0x6f, 0x75, 0x62, 0x74, +} + +type hashed struct { + hash []byte + salt []byte + cost uint32 // allowed range is MinCost to MaxCost + major byte + minor byte +} + +// GenerateFromPassword returns the bcrypt hash of the password at the given +// cost. If the cost given is less than MinCost, the cost will be set to +// MinCost, instead. Use CompareHashAndPassword, as defined in this package, +// to compare the returned hashed password with its cleartext version. +func GenerateFromPassword(password []byte, cost int) ([]byte, os.Error) { + p, err := newFromPassword(password, cost) + if err != nil { + return nil, err + } + return p.Hash(), nil +} + +// CompareHashAndPassword compares a bcrypt hashed password with its possible +// plaintext equivalent. Note: Using bytes.Equal for this job is +// insecure. Returns nil on success, or an error on failure. +func CompareHashAndPassword(hashedPassword, password []byte) os.Error { + p, err := newFromHash(hashedPassword) + if err != nil { + return err + } + + otherHash, err := bcrypt(password, p.cost, p.salt) + if err != nil { + return err + } + + otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} + if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { + return nil + } + + return MismatchedHashAndPasswordError +} + +func newFromPassword(password []byte, cost int) (*hashed, os.Error) { + if cost < MinCost { + cost = DefaultCost + } + p := new(hashed) + p.major = majorVersion + p.minor = minorVersion + + err := checkCost(cost) + if err != nil { + return nil, err + } + p.cost = uint32(cost) + + unencodedSalt := make([]byte, maxSaltSize) + _, err = io.ReadFull(rand.Reader, unencodedSalt) + if err != nil { + return nil, err + } + + p.salt = base64Encode(unencodedSalt) + hash, err := bcrypt(password, p.cost, p.salt) + if err != nil { + return nil, err + } + p.hash = hash + return p, err +} + +func newFromHash(hashedSecret []byte) (*hashed, os.Error) { + if len(hashedSecret) < minHashSize { + return nil, HashTooShortError + } + p := new(hashed) + n, err := p.decodeVersion(hashedSecret) + if err != nil { + return nil, err + } + hashedSecret = hashedSecret[n:] + n, err = p.decodeCost(hashedSecret) + if err != nil { + return nil, err + } + hashedSecret = hashedSecret[n:] + + // The "+2" is here because we'll have to append at most 2 '=' to the salt + // when base64 decoding it in expensiveBlowfishSetup(). + p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) + copy(p.salt, hashedSecret[:encodedSaltSize]) + + hashedSecret = hashedSecret[encodedSaltSize:] + p.hash = make([]byte, len(hashedSecret)) + copy(p.hash, hashedSecret) + + return p, nil +} + +func bcrypt(password []byte, cost uint32, salt []byte) ([]byte, os.Error) { + cipherData := make([]byte, len(magicCipherData)) + copy(cipherData, magicCipherData) + + c, err := expensiveBlowfishSetup(password, cost, salt) + if err != nil { + return nil, err + } + + for i := 0; i < 24; i += 8 { + for j := 0; j < 64; j++ { + c.Encrypt(cipherData[i:i+8], cipherData[i:i+8]) + } + } + + // Bug compatibility with C bcrypt implementations. We only encode 23 of + // the 24 bytes encrypted. + hsh := base64Encode(cipherData[:maxCryptedHashSize]) + return hsh, nil +} + +func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, os.Error) { + + csalt, err := base64Decode(salt) + if err != nil { + return nil, err + } + + // Bug compatibility with C bcrypt implementations. They use the trailing + // NULL in the key string during expansion. + ckey := append(key, 0) + + c, err := blowfish.NewSaltedCipher(ckey, csalt) + if err != nil { + return nil, err + } + + rounds := 1 << cost + for i := 0; i < rounds; i++ { + blowfish.ExpandKey(ckey, c) + blowfish.ExpandKey(csalt, c) + } + + return c, nil +} + +func (p *hashed) Hash() []byte { + arr := make([]byte, 60) + arr[0] = '$' + arr[1] = p.major + n := 2 + if p.minor != 0 { + arr[2] = p.minor + n = 3 + } + arr[n] = '$' + n += 1 + copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost))) + n += 2 + arr[n] = '$' + n += 1 + copy(arr[n:], p.salt) + n += encodedSaltSize + copy(arr[n:], p.hash) + n += encodedHashSize + return arr[:n] +} + +func (p *hashed) decodeVersion(sbytes []byte) (int, os.Error) { + if sbytes[0] != '$' { + return -1, InvalidHashPrefixError(sbytes[0]) + } + if sbytes[1] > majorVersion { + return -1, HashVersionTooNewError(sbytes[1]) + } + p.major = sbytes[1] + n := 3 + if sbytes[2] != '$' { + p.minor = sbytes[2] + n++ + } + return n, nil +} + +// sbytes should begin where decodeVersion left off. +func (p *hashed) decodeCost(sbytes []byte) (int, os.Error) { + cost, err := strconv.Atoi(string(sbytes[0:2])) + if err != nil { + return -1, err + } + err = checkCost(cost) + if err != nil { + return -1, err + } + p.cost = uint32(cost) + return 3, nil +} + +func (p *hashed) String() string { + return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) +} + +func checkCost(cost int) os.Error { + if cost < MinCost || cost > MaxCost { + return InvalidCostError(cost) + } + return nil +} diff --git a/src/pkg/crypto/bcrypt/bcrypt_test.go b/src/pkg/crypto/bcrypt/bcrypt_test.go new file mode 100644 index 000000000..89eca0a44 --- /dev/null +++ b/src/pkg/crypto/bcrypt/bcrypt_test.go @@ -0,0 +1,195 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bcrypt + +import ( + "bytes" + "os" + "testing" +) + +func TestBcryptingIsEasy(t *testing.T) { + pass := []byte("mypassword") + hp, err := GenerateFromPassword(pass, 0) + if err != nil { + t.Fatalf("GenerateFromPassword error: %s", err) + } + + if CompareHashAndPassword(hp, pass) != nil { + t.Errorf("%v should hash %s correctly", hp, pass) + } + + notPass := "notthepass" + err = CompareHashAndPassword(hp, []byte(notPass)) + if err != MismatchedHashAndPasswordError { + t.Errorf("%v and %s should be mismatched", hp, notPass) + } +} + +func TestBcryptingIsCorrect(t *testing.T) { + pass := []byte("allmine") + salt := []byte("XajjQvNhvvRt5GSeFk1xFe") + expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") + + hash, err := bcrypt(pass, 10, salt) + if err != nil { + t.Fatalf("bcrypt blew up: %v", err) + } + if !bytes.HasSuffix(expectedHash, hash) { + t.Errorf("%v should be the suffix of %v", hash, expectedHash) + } + + h, err := newFromHash(expectedHash) + if err != nil { + t.Errorf("Unable to parse %s: %v", string(expectedHash), err) + } + + // This is not the safe way to compare these hashes. We do this only for + // testing clarity. Use bcrypt.CompareHashAndPassword() + if err == nil && !bytes.Equal(expectedHash, h.Hash()) { + t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash) + } +} + +func TestTooLongPasswordsWork(t *testing.T) { + salt := []byte("XajjQvNhvvRt5GSeFk1xFe") + // One byte over the usual 56 byte limit that blowfish has + tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456") + tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C") + hash, err := bcrypt(tooLongPass, 10, salt) + if err != nil { + t.Fatalf("bcrypt blew up on long password: %v", err) + } + if !bytes.HasSuffix(tooLongExpected, hash) { + t.Errorf("%v should be the suffix of %v", hash, tooLongExpected) + } +} + +type InvalidHashTest struct { + err os.Error + hash []byte +} + +var invalidTests = []InvalidHashTest{ + {HashTooShortError, []byte("$2a$10$fooo")}, + {HashTooShortError, []byte("$2a")}, + {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, + {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, + {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, +} + +func TestInvalidHashErrors(t *testing.T) { + check := func(name string, expected, err os.Error) { + if err == nil { + t.Errorf("%s: Should have returned an error", name) + } + if err != nil && err != expected { + t.Errorf("%s gave err %v but should have given %v", name, err.String(), expected.String()) + } + } + for _, iht := range invalidTests { + _, err := newFromHash(iht.hash) + check("newFromHash", iht.err, err) + err = CompareHashAndPassword(iht.hash, []byte("anything")) + check("CompareHashAndPassword", iht.err, err) + } +} + +func TestUnpaddedBase64Encoding(t *testing.T) { + original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30} + encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") + + encoded := base64Encode(original) + + if !bytes.Equal(encodedOriginal, encoded) { + t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal) + } + + decoded, err := base64Decode(encodedOriginal) + if err != nil { + t.Fatalf("base64Decode blew up: %s", err) + } + + if !bytes.Equal(decoded, original) { + t.Errorf("Decoded %v should have equaled %v", decoded, original) + } +} + +func TestCost(t *testing.T) { + if testing.Short() { + return + } + + pass := []byte("mypassword") + + for c := 0; c < MinCost; c++ { + p, _ := newFromPassword(pass, c) + if p.cost != uint32(DefaultCost) { + t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost) + } + } + + p, _ := newFromPassword(pass, 14) + if p.cost != 14 { + t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost) + } + + hp, _ := newFromHash(p.Hash()) + if p.cost != hp.cost { + t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost) + } + + _, err := newFromPassword(pass, 32) + if err == nil { + t.Fatalf("newFromPassword: should return a cost error") + } + if err != InvalidCostError(32) { + t.Errorf("newFromPassword: should return cost error, got %#v", err) + } +} + +func TestCostReturnsWithLeadingZeroes(t *testing.T) { + hp, _ := newFromPassword([]byte("abcdefgh"), 7) + cost := hp.Hash()[4:7] + expected := []byte("07$") + + if !bytes.Equal(expected, cost) { + t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected) + } +} + +func TestMinorNotRequired(t *testing.T) { + noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") + h, err := newFromHash(noMinorHash) + if err != nil { + t.Fatalf("No minor hash blew up: %s", err) + } + if h.minor != 0 { + t.Errorf("Should leave minor version at 0, but was %d", h.minor) + } + + if !bytes.Equal(noMinorHash, h.Hash()) { + t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash()) + } +} + +func BenchmarkEqual(b *testing.B) { + b.StopTimer() + passwd := []byte("somepasswordyoulike") + hash, _ := GenerateFromPassword(passwd, 10) + b.StartTimer() + for i := 0; i < b.N; i++ { + CompareHashAndPassword(hash, passwd) + } +} + +func BenchmarkGeneration(b *testing.B) { + b.StopTimer() + passwd := []byte("mylongpassword1234") + b.StartTimer() + for i := 0; i < b.N; i++ { + GenerateFromPassword(passwd, 10) + } +} diff --git a/src/pkg/crypto/blowfish/block.go b/src/pkg/crypto/blowfish/block.go index 7fbe7eefb..326292dfc 100644 --- a/src/pkg/crypto/blowfish/block.go +++ b/src/pkg/crypto/blowfish/block.go @@ -4,13 +4,12 @@ package blowfish -func expandKey(key []byte, c *Cipher) { - copy(c.p[0:], p[0:]) - copy(c.s0[0:], s0[0:]) - copy(c.s1[0:], s1[0:]) - copy(c.s2[0:], s2[0:]) - copy(c.s3[0:], s3[0:]) - +// ExpandKey performs a key expansion on the given *Cipher. Specifically, it +// performs the Blowfish algorithm's key schedule which sets up the *Cipher's +// pi and substitution tables for calls to Encrypt. This is used, primarily, +// by the bcrypt package to reuse the Blowfish key schedule during its +// set up. It's unlikely that you need to use this directly. +func ExpandKey(key []byte, c *Cipher) { j := 0 for i := 0; i < 18; i++ { var d uint32 @@ -48,6 +47,98 @@ func expandKey(key []byte, c *Cipher) { } } +// This is similar to ExpandKey, but folds the salt during the key +// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero +// salt passed in, reusing ExpandKey turns out to be a place of inefficiency +// and specializing it here is useful. +func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { + j := 0 + expandedKey := make([]uint32, 18) + for i := 0; i < 18; i++ { + var d uint32 + for k := 0; k < 4; k++ { + d = d<<8 | uint32(key[j])&0x000000FF + j++ + if j >= len(key) { + j = 0 + } + } + expandedKey[i] = d + c.p[i] ^= d + } + + j = 0 + expandedSalt := make([]uint32, 18) + for i := 0; i < 18; i++ { + var d uint32 + for k := 0; k < 4; k++ { + d = d<<8 | uint32(salt[j])&0x000000FF + j++ + if j >= len(salt) { + j = 0 + } + } + expandedSalt[i] = d + } + + var l, r uint32 + for i := 0; i < 18; i += 2 { + l ^= expandedSalt[i&2] + r ^= expandedSalt[(i&2)+1] + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 4 { + l ^= expandedSalt[2] + r ^= expandedSalt[3] + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + + l ^= expandedSalt[0] + r ^= expandedSalt[1] + l, r = encryptBlock(l, r, c) + c.s0[i+2], c.s0[i+3] = l, r + + } + + for i := 0; i < 256; i += 4 { + l ^= expandedSalt[2] + r ^= expandedSalt[3] + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + + l ^= expandedSalt[0] + r ^= expandedSalt[1] + l, r = encryptBlock(l, r, c) + c.s1[i+2], c.s1[i+3] = l, r + } + + for i := 0; i < 256; i += 4 { + l ^= expandedSalt[2] + r ^= expandedSalt[3] + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + + l ^= expandedSalt[0] + r ^= expandedSalt[1] + l, r = encryptBlock(l, r, c) + c.s2[i+2], c.s2[i+3] = l, r + } + + for i := 0; i < 256; i += 4 { + l ^= expandedSalt[2] + r ^= expandedSalt[3] + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + + l ^= expandedSalt[0] + r ^= expandedSalt[1] + l, r = encryptBlock(l, r, c) + c.s3[i+2], c.s3[i+3] = l, r + } +} + func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { xl, xr := l, r xl ^= c.p[0] diff --git a/src/pkg/crypto/blowfish/blowfish_test.go b/src/pkg/crypto/blowfish/blowfish_test.go index 3a7ab6c2a..1038d2e39 100644 --- a/src/pkg/crypto/blowfish/blowfish_test.go +++ b/src/pkg/crypto/blowfish/blowfish_test.go @@ -190,3 +190,21 @@ func TestCipherDecrypt(t *testing.T) { } } } + +func TestSaltedCipherKeyLength(t *testing.T) { + var key []byte + for i := 0; i < 4; i++ { + _, err := NewSaltedCipher(key, []byte{'a'}) + if err != KeySizeError(i) { + t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(i)) + } + key = append(key, 'a') + } + + // A 57-byte key. One over the typical blowfish restriction. + key = []byte("012345678901234567890123456789012345678901234567890123456") + _, err := NewSaltedCipher(key, []byte{'a'}) + if err != nil { + t.Errorf("NewSaltedCipher with long key, gave error %#v", err) + } +} diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go index 6c37dfe94..3439825e8 100644 --- a/src/pkg/crypto/blowfish/cipher.go +++ b/src/pkg/crypto/blowfish/cipher.go @@ -31,12 +31,28 @@ func (k KeySizeError) String() string { // NewCipher creates and returns a Cipher. // The key argument should be the Blowfish key, 4 to 56 bytes. func NewCipher(key []byte) (*Cipher, os.Error) { + var result Cipher k := len(key) if k < 4 || k > 56 { return nil, KeySizeError(k) } + initCipher(key, &result) + ExpandKey(key, &result) + return &result, nil +} + +// NewSaltedCipher creates a returns a Cipher that folds a salt into its key +// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is +// sufficient and desirable. For bcrypt compatiblity, the key can be over 56 +// bytes. +func NewSaltedCipher(key, salt []byte) (*Cipher, os.Error) { var result Cipher - expandKey(key, &result) + k := len(key) + if k < 4 { + return nil, KeySizeError(k) + } + initCipher(key, &result) + expandKeyWithSalt(key, salt, &result) return &result, nil } @@ -77,3 +93,11 @@ func (c *Cipher) Reset() { zero(c.s2[0:]) zero(c.s3[0:]) } + +func initCipher(key []byte, c *Cipher) { + copy(c.p[0:], p[0:]) + copy(c.s0[0:], s0[0:]) + copy(c.s1[0:], s1[0:]) + copy(c.s2[0:], s2[0:]) + copy(c.s3[0:], s3[0:]) +} diff --git a/src/pkg/crypto/ocsp/ocsp_test.go b/src/pkg/crypto/ocsp/ocsp_test.go index f9889790f..7be37211c 100644 --- a/src/pkg/crypto/ocsp/ocsp_test.go +++ b/src/pkg/crypto/ocsp/ocsp_test.go @@ -15,7 +15,7 @@ func TestOCSPDecode(t *testing.T) { t.Error(err) } - expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}} + expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}} if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go index 3a06aa8b1..76a7365b7 100644 --- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Unix cryptographically secure pseudorandom number // generator. diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go index bc7b0d32f..77e712da1 100644 --- a/src/pkg/crypto/tls/cipher_suites.go +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -9,6 +9,7 @@ import ( "crypto/cipher" "crypto/hmac" "crypto/rc4" + "crypto/sha1" "crypto/x509" "hash" "os" @@ -23,7 +24,7 @@ type keyAgreement interface { // ServerKeyExchange message, generateServerKeyExchange can return nil, // nil. generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) - processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error) + processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, os.Error) // On the client side, the next two methods are called in order. @@ -46,14 +47,14 @@ type cipherSuite struct { // and point format that we can handle. elliptic bool cipher func(key, iv []byte, isRead bool) interface{} - mac func(macKey []byte) hash.Hash + mac func(version uint16, macKey []byte) macFunction } var cipherSuites = map[uint16]*cipherSuite{ - TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1}, - TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1}, - TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1}, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1}, + TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, + TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1}, + TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -69,8 +70,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { return cipher.NewCBCEncrypter(block, iv) } -func hmacSHA1(key []byte) hash.Hash { - return hmac.NewSHA1(key) +// macSHA1 returns a macFunction for the given protocol version. +func macSHA1(version uint16, key []byte) macFunction { + if version == versionSSL30 { + mac := ssl30MAC{ + h: sha1.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.NewSHA1(key)} +} + +type macFunction interface { + Size() int + MAC(seq, data []byte) []byte +} + +// ssl30MAC implements the SSLv3 MAC function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 +type ssl30MAC struct { + h hash.Hash + key []byte +} + +func (s ssl30MAC) Size() int { + return s.h.Size() +} + +var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} + +var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} + +func (s ssl30MAC) MAC(seq, record []byte) []byte { + padLength := 48 + if s.h.Size() == 20 { + padLength = 40 + } + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad1[:padLength]) + s.h.Write(seq) + s.h.Write(record[:1]) + s.h.Write(record[3:5]) + s.h.Write(record[recordHeaderLen:]) + digest := s.h.Sum() + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad2[:padLength]) + s.h.Write(digest) + return s.h.Sum() +} + +// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. +type tls10MAC struct { + h hash.Hash +} + +func (s tls10MAC) Size() int { + return s.h.Size() +} + +func (s tls10MAC) MAC(seq, record []byte) []byte { + s.h.Reset() + s.h.Write(seq) + s.h.Write(record) + return s.h.Sum() } func rsaKA() keyAgreement { diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 3efac9c13..8fb1a8848 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -20,8 +20,11 @@ const ( recordHeaderLen = 5 // record header length maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - minVersion = 0x0301 // minimum supported version - TLS 1.0 - maxVersion = 0x0301 // maximum supported version - TLS 1.0 + versionSSL30 = 0x0300 + versionTLS10 = 0x0301 + + minVersion = versionSSL30 + maxVersion = versionTLS10 ) // TLS record types. diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index fac65afd9..07199515d 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -11,7 +11,6 @@ import ( "crypto/cipher" "crypto/subtle" "crypto/x509" - "hash" "io" "net" "os" @@ -108,18 +107,20 @@ func (c *Conn) SetWriteTimeout(nsec int64) os.Error { // connection, either sending or receiving. type halfConn struct { sync.Mutex - cipher interface{} // cipher algorithm - mac hash.Hash // MAC algorithm - seq [8]byte // 64-bit sequence number - bfree *block // list of free blocks + version uint16 // protocol version + cipher interface{} // cipher algorithm + mac macFunction + seq [8]byte // 64-bit sequence number + bfree *block // list of free blocks nextCipher interface{} // next encryption state - nextMac hash.Hash // next MAC algorithm + nextMac macFunction // next MAC algorithm } // prepareCipherSpec sets the encryption and MAC states // that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) { +func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { + hc.version = version hc.nextCipher = cipher hc.nextMac = mac } @@ -197,6 +198,22 @@ func removePadding(payload []byte) ([]byte, byte) { return payload[:len(payload)-int(toRemove)], good } +// removePaddingSSL30 is a replacement for removePadding in the case that the +// protocol version is SSLv3. In this version, the contents of the padding +// are random and cannot be checked. +func removePaddingSSL30(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := int(payload[len(payload)-1]) + 1 + if paddingLen > len(payload) { + return payload, 0 + } + + return payload[:len(payload)-paddingLen], 255 +} + func roundUp(a, b int) int { return a + (b-a%b)%b } @@ -226,7 +243,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { } c.CryptBlocks(payload, payload) - payload, paddingGood = removePadding(payload) + if hc.version == versionSSL30 { + payload, paddingGood = removePaddingSSL30(payload) + } else { + payload, paddingGood = removePadding(payload) + } b.resize(recordHeaderLen + len(payload)) // note that we still have a timing side-channel in the @@ -256,13 +277,10 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { b.data[4] = byte(n) b.resize(recordHeaderLen + n) remoteMAC := payload[n:] - - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + localMAC := hc.mac.MAC(hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 { + if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { return false, alertBadRecordMAC } } @@ -291,11 +309,9 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { func (hc *halfConn) encrypt(b *block) (bool, alert) { // mac if hc.mac != nil { - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + mac := hc.mac.MAC(hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - mac := hc.mac.Sum() + n := len(b.data) b.resize(n + len(mac)) copy(b.data[n:], mac) @@ -470,6 +486,19 @@ Again: if n > maxCiphertext { return c.sendAlert(alertRecordOverflow) } + if !c.haveVers { + // First message, be extra suspicious: + // this might not be a TLS client. + // Bail out before reading a full 'body', if possible. + // The current max version is 3.1. + // If the version is >= 16.0, it's probably not real. + // Similarly, a clientHello message encodes in + // well under a kilobyte. If the length is >= 12 kB, + // it's probably not real. + if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 { + return c.sendAlert(alertUnexpectedMessage) + } + } if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index 15604cea7..0badc39c4 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -14,7 +14,7 @@ import ( ) func (c *Conn) clientHandshake() os.Error { - finishedHash := newFinishedHash() + finishedHash := newFinishedHash(versionTLS10) if c.config == nil { c.config = defaultConfig() @@ -247,11 +247,11 @@ func (c *Conn) clientHandshake() os.Error { } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) - clientHash := suite.mac(clientMAC) - c.out.prepareCipherSpec(clientCipher, clientHash) + clientHash := suite.mac(c.vers, clientMAC) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) if serverHello.nextProtoNeg { @@ -271,8 +271,8 @@ func (c *Conn) clientHandshake() os.Error { c.writeRecord(recordTypeHandshake, finished.marshal()) serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) - serverHash := suite.mac(serverMAC) - c.in.prepareCipherSpec(serverCipher, serverHash) + serverHash := suite.mac(c.vers, serverMAC) + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if c.err != nil { return c.err diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go index 3f91c7acf..c0abcda20 100644 --- a/src/pkg/crypto/tls/handshake_client_test.go +++ b/src/pkg/crypto/tls/handshake_client_test.go @@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * go func() { cli.Write([]byte("hello\n")) cli.Close() + c.Close() }() defer c.Close() diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index 6645adce4..f11232d8e 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -676,9 +676,9 @@ func (m *finishedMsg) marshal() (x []byte) { return m.raw } - x = make([]byte, 16) + x = make([]byte, 4+len(m.verifyData)) x[0] = typeFinished - x[3] = 12 + x[3] = byte(len(m.verifyData)) copy(x[4:], m.verifyData) m.raw = x return @@ -686,7 +686,7 @@ func (m *finishedMsg) marshal() (x []byte) { func (m *finishedMsg) unmarshal(data []byte) bool { m.raw = data - if len(data) != 4+12 { + if len(data) < 4 { return false } m.verifyData = data[4:] diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go index 23f729dd9..dc68a1223 100644 --- a/src/pkg/crypto/tls/handshake_messages_test.go +++ b/src/pkg/crypto/tls/handshake_messages_test.go @@ -14,13 +14,13 @@ import ( var tests = []interface{}{ &clientHelloMsg{}, &serverHelloMsg{}, + &finishedMsg{}, &certificateMsg{}, &certificateRequestMsg{}, &certificateVerifyMsg{}, &certificateStatusMsg{}, &clientKeyExchangeMsg{}, - &finishedMsg{}, &nextProtoMsg{}, } @@ -59,11 +59,12 @@ func TestMarshalUnmarshal(t *testing.T) { break } - if i >= 2 { - // The first two message types (ClientHello and - // ServerHello) are allowed to have parsable - // prefixes because the extension data is - // optional. + if i >= 3 { + // The first three message types (ClientHello, + // ServerHello and Finished) are allowed to + // have parsable prefixes because the extension + // data is optional and the length of the + // Finished varies across versions. for j := 0; j < len(marshaled); j++ { if m2.unmarshal(marshaled[0:j]) { t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1) diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 44a324041..f083a873d 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error { c.vers = vers c.haveVers = true - finishedHash := newFinishedHash() + finishedHash := newFinishedHash(vers) finishedHash.Write(clientHello.marshal()) hello := new(serverHelloMsg) @@ -128,7 +128,6 @@ FindCipherSuite: } keyAgreement := suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello) if err != nil { c.sendAlert(alertHandshakeFailure) @@ -235,18 +234,18 @@ FindCipherSuite: finishedHash.Write(certVerify.marshal()) } - preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx) + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers) if err != nil { c.sendAlert(alertHandshakeFailure) return err } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) - clientHash := suite.mac(clientMAC) - c.in.prepareCipherSpec(clientCipher, clientHash) + clientHash := suite.mac(c.vers, clientMAC) + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err @@ -283,8 +282,8 @@ FindCipherSuite: finishedHash.Write(clientFinished.marshal()) serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) - serverHash := suite.mac(serverMAC) - c.out.prepareCipherSpec(serverCipher, serverHash) + serverHash := suite.mac(c.vers, serverMAC) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go index b77646e43..d910eadcd 100644 --- a/src/pkg/crypto/tls/handshake_server_test.go +++ b/src/pkg/crypto/tls/handshake_server_test.go @@ -62,7 +62,7 @@ func TestSimpleError(t *testing.T) { testClientHelloFailure(t, &serverHelloDoneMsg{}, alertUnexpectedMessage) } -var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205, 0x0300} +var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205} func TestRejectBadProtocolVersion(t *testing.T) { for _, v := range badProtocolVersions { @@ -112,6 +112,7 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config * go func() { srv.Write([]byte("hello, world\n")) srv.Close() + s.Close() }() defer c.Close() @@ -121,9 +122,9 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config * continue } bb := make([]byte, len(b)) - _, err := io.ReadFull(c, bb) + n, err := io.ReadFull(c, bb) if err != nil { - t.Fatalf("%s #%d: %s", name, i, err) + t.Fatalf("%s #%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", name, i, err, n, len(bb), bb[:n], b) } if !bytes.Equal(b, bb) { t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b) @@ -142,6 +143,10 @@ func TestHandshakeServerAES(t *testing.T) { testServerScript(t, "AES", aesServerScript, aesConfig) } +func TestHandshakeServerSSLv3(t *testing.T) { + testServerScript(t, "SSLv3", sslv3ServerScript, testConfig) +} + var serve = flag.Bool("serve", false, "run a TLS server on :10443") func TestRunServer(t *testing.T) { @@ -515,3 +520,165 @@ var aesServerScript = [][]byte{ 0xcd, 0x84, 0xf0, }, } + +var sslv3ServerScript = [][]byte{ + { + 0x16, 0x03, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, + 0x3d, 0x03, 0x00, 0x4e, 0x70, 0xe2, 0x18, 0x86, + 0xd6, 0xc6, 0x6f, 0xf3, 0xc8, 0xf4, 0x02, 0xd6, + 0x4d, 0xee, 0x17, 0x32, 0x4b, 0xd2, 0x78, 0xd8, + 0xa1, 0x03, 0x5d, 0x68, 0x82, 0x89, 0xbe, 0xfd, + 0x12, 0xb9, 0x06, 0x00, 0x00, 0x16, 0x00, 0x33, + 0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, + 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + }, + + { + 0x16, 0x03, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x00, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, + 0x80, 0x74, 0x0e, 0x3a, 0xcf, 0xba, 0x9f, 0x1a, + 0x9b, 0xb2, 0xa4, 0xc7, 0x5d, 0xf3, 0x0c, 0x80, + 0x06, 0x80, 0xf3, 0x57, 0xb2, 0xd9, 0x36, 0x24, + 0x6a, 0x06, 0x13, 0x40, 0xf9, 0x7c, 0xb9, 0x3e, + 0x4b, 0x68, 0x4f, 0x21, 0x90, 0x2d, 0xbd, 0xca, + 0xd4, 0x83, 0xf0, 0x7a, 0xeb, 0x7a, 0x74, 0x1b, + 0xcd, 0xfe, 0x69, 0xef, 0xc0, 0x86, 0xa0, 0x24, + 0x31, 0x65, 0x40, 0xd2, 0xdd, 0x6f, 0xb9, 0xd7, + 0x8d, 0xc1, 0x69, 0x60, 0x44, 0x7a, 0x75, 0xfb, + 0x42, 0x6a, 0x0f, 0x66, 0x45, 0x10, 0x73, 0xee, + 0x87, 0x28, 0x37, 0x83, 0x86, 0xd8, 0x5a, 0xc8, + 0x60, 0x87, 0xda, 0x33, 0x87, 0xaf, 0x34, 0x8b, + 0xf5, 0x61, 0x63, 0x7a, 0x5c, 0x60, 0x26, 0xb9, + 0xdb, 0xa1, 0xb7, 0xe3, 0x60, 0x38, 0x94, 0x5c, + 0x83, 0x23, 0xd6, 0x8d, 0xc2, 0x14, 0x4a, 0x0f, + 0x0e, 0x4f, 0xf9, 0x4e, 0x7b, 0x15, 0xcd, 0x18, + 0x04, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, + 0x03, 0x00, 0x00, 0x3c, 0xbd, 0xbc, 0xec, 0xdc, + 0x79, 0xb1, 0xae, 0x16, 0xc9, 0x26, 0x9a, 0xc0, + 0xc0, 0x2c, 0x33, 0x36, 0x13, 0x91, 0x58, 0x5d, + 0x7d, 0xee, 0x4e, 0xd8, 0x7e, 0xac, 0x88, 0x87, + 0x0a, 0x75, 0x66, 0xb1, 0x44, 0x79, 0x2f, 0x42, + 0xe8, 0x92, 0x74, 0x4c, 0xab, 0x36, 0xc8, 0x17, + 0x5f, 0x02, 0x8a, 0x20, 0x53, 0xe9, 0x1d, 0xb4, + 0xfe, 0x5c, 0x2b, 0xd9, 0x0a, 0xfb, 0xc6, 0x63, + }, + + { + 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x00, 0x00, 0x3c, 0xaa, 0xa1, 0x98, 0xc4, 0x6b, + 0x5a, 0x16, 0x3f, 0x5f, 0xa4, 0x96, 0x3e, 0x78, + 0xe4, 0x6f, 0x49, 0x05, 0x47, 0xc4, 0x05, 0x60, + 0xeb, 0x0b, 0x45, 0xe3, 0xbc, 0x50, 0x11, 0x24, + 0x5f, 0x01, 0xd7, 0xb8, 0x8f, 0x60, 0x63, 0x66, + 0xbd, 0x3e, 0xd9, 0xa8, 0x80, 0x43, 0x9f, 0x0b, + 0x51, 0x61, 0xed, 0x13, 0xc6, 0x21, 0xd0, 0xfe, + 0xbc, 0x17, 0x3c, 0x36, 0xb0, 0x82, 0x7f, 0x17, + 0x03, 0x00, 0x00, 0x21, 0xee, 0x44, 0xf3, 0xa6, + 0x88, 0x9d, 0x78, 0x44, 0xde, 0xdf, 0xeb, 0xc5, + 0xad, 0xc4, 0xcc, 0x56, 0x5c, 0x54, 0x96, 0x52, + 0x3f, 0xd9, 0x40, 0x6e, 0x79, 0xd8, 0x58, 0x78, + 0x4f, 0x5a, 0xe9, 0x06, 0xef, 0x15, 0x03, 0x00, + 0x00, 0x16, 0xd3, 0xc2, 0x52, 0x99, 0x2a, 0x84, + 0xc4, 0x52, 0x5f, 0x3b, 0x19, 0xe7, 0xfc, 0x65, + 0xaf, 0xd3, 0xb7, 0xa3, 0xcc, 0x4a, 0x1d, 0x2e, + }, +} diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index a40d18fd9..e347528b5 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -24,7 +24,7 @@ func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello return nil, nil } -func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) { preMasterSecret := make([]byte, 48) _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) if err != nil { @@ -34,11 +34,15 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe if len(ckx.ciphertext) < 2 { return nil, os.NewError("bad ClientKeyExchange") } - ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) - if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, os.NewError("bad ClientKeyExchange") + + ciphertext := ckx.ciphertext + if version != versionSSL30 { + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, os.NewError("bad ClientKeyExchange") + } + ciphertext = ckx.ciphertext[2:] } - ciphertext := ckx.ciphertext[2:] err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret) if err != nil { @@ -159,7 +163,7 @@ Curve: return skx, nil } -func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { return nil, os.NewError("bad ClientKeyExchange") } diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go index 478cf65f9..2d58dc520 100644 --- a/src/pkg/crypto/tls/prf.go +++ b/src/pkg/crypto/tls/prf.go @@ -63,6 +63,39 @@ func pRF10(result, secret, label, seed []byte) { } } +// pRF30 implements the SSL 3.0 pseudo-random function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. +func pRF30(result, secret, label, seed []byte) { + hashSHA1 := sha1.New() + hashMD5 := md5.New() + + done := 0 + i := 0 + // RFC5246 section 6.3 says that the largest PRF output needed is 128 + // bytes. Since no more ciphersuites will be added to SSLv3, this will + // remain true. Each iteration gives us 16 bytes so 10 iterations will + // be sufficient. + var b [11]byte + for done < len(result) { + for j := 0; j <= i; j++ { + b[j] = 'A' + byte(i) + } + + hashSHA1.Reset() + hashSHA1.Write(b[:i+1]) + hashSHA1.Write(secret) + hashSHA1.Write(seed) + digest := hashSHA1.Sum() + + hashMD5.Reset() + hashMD5.Write(secret) + hashMD5.Write(digest) + + done += copy(result[done:], hashMD5.Sum()) + i++ + } +} + const ( tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. masterSecretLength = 48 // Length of a master secret in TLS 1.1. @@ -77,19 +110,24 @@ var serverFinishedLabel = []byte("server finished") // keysFromPreMasterSecret generates the connection keys from the pre master // secret, given the lengths of the MAC key, cipher key and IV, as defined in // RFC 2246, section 6.3. -func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { +func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + prf := pRF10 + if version == versionSSL30 { + prf = pRF30 + } + var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret = make([]byte, masterSecretLength) - pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) - pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -104,6 +142,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt return } +func newFinishedHash(version uint16) finishedHash { + return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} +} + // A finishedHash calculates the hash of a set of handshake messages suitable // for including in a Finished message. type finishedHash struct { @@ -111,10 +153,7 @@ type finishedHash struct { clientSHA1 hash.Hash serverMD5 hash.Hash serverSHA1 hash.Hash -} - -func newFinishedHash() finishedHash { - return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()} + version uint16 } func (h finishedHash) Write(msg []byte) (n int, err os.Error) { @@ -125,9 +164,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) { return len(msg), nil } -// finishedSum calculates the contents of the verify_data member of a Finished -// message given the MD5 and SHA1 hashes of a set of handshake messages. -func finishedSum(md5, sha1, label, masterSecret []byte) []byte { +// finishedSum10 calculates the contents of the verify_data member of a TLSv1 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { seed := make([]byte, len(md5)+len(sha1)) copy(seed, md5) copy(seed[len(md5):], sha1) @@ -136,18 +176,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte { return out } +// finishedSum30 calculates the contents of the verify_data member of a SSLv3 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte { + md5.Write(magic[:]) + md5.Write(masterSecret) + md5.Write(ssl30Pad1[:]) + md5Digest := md5.Sum() + + md5.Reset() + md5.Write(masterSecret) + md5.Write(ssl30Pad2[:]) + md5.Write(md5Digest) + md5Digest = md5.Sum() + + sha1.Write(magic[:]) + sha1.Write(masterSecret) + sha1.Write(ssl30Pad1[:40]) + sha1Digest := sha1.Sum() + + sha1.Reset() + sha1.Write(masterSecret) + sha1.Write(ssl30Pad2[:40]) + sha1.Write(sha1Digest) + sha1Digest = sha1.Sum() + + ret := make([]byte, len(md5Digest)+len(sha1Digest)) + copy(ret, md5Digest) + copy(ret[len(md5Digest):], sha1Digest) + return ret +} + +var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} +var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} + // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { + if h.version == versionSSL30 { + return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) + } + md5 := h.clientMD5.Sum() sha1 := h.clientSHA1.Sum() - return finishedSum(md5, sha1, clientFinishedLabel, masterSecret) + return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { + if h.version == versionSSL30 { + return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) + } + md5 := h.serverMD5.Sum() sha1 := h.serverSHA1.Sum() - return finishedSum(md5, sha1, serverFinishedLabel, masterSecret) + return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) } diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go index f8c4acb9d..a32392cef 100644 --- a/src/pkg/crypto/tls/prf_test.go +++ b/src/pkg/crypto/tls/prf_test.go @@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) { } type testKeysFromTest struct { + version uint16 preMasterSecret string clientRandom, serverRandom string masterSecret string @@ -47,7 +48,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { in, _ := hex.DecodeString(test.preMasterSecret) clientRandom, _ := hex.DecodeString(test.clientRandom) serverRandom, _ := hex.DecodeString(test.serverRandom) - master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) + master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) masterString := hex.EncodeToString(master) clientMACString := hex.EncodeToString(clientMAC) serverMACString := hex.EncodeToString(serverMAC) @@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { serverMACString != test.serverMAC || clientKeyString != test.clientKey || serverKeyString != test.serverKey { - t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) + t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) } } } @@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` var testKeysFromTests = []testKeysFromTest{ { + versionTLS10, "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", @@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", @@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", @@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{ 20, 16, }, + { + versionSSL30, + "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", + "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", + "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", + "a614863e56299dcffeea2938f22c2ba023768dbe4b3f6877bc9c346c6ae529b51d9cb87ff9695ea4d01f2205584405b2", + "2c450d5b6f6e2013ac6bea6a0b32200d4e1ffb94", + "7a7a7438769536f2fb1ae49a61f0703b79b2dc53", + "f8f6b26c10f12855c9aafb1e0e839ccf", + "2b9d4b4a60cb7f396780ebff50650419", + 20, + 16, + }, } diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 451d3d514..98f2723c8 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -227,6 +227,9 @@ func TestNoSectionOverlaps(t *testing.T) { } for i, si := range f.Sections { sih := si.SectionHeader + if sih.Type == SHT_NOBITS { + continue + } for j, sj := range f.Sections { sjh := sj.SectionHeader if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { diff --git a/src/pkg/exec/lp_unix.go b/src/pkg/exec/lp_unix.go index 008fb11a8..0cd19e7ac 100644 --- a/src/pkg/exec/lp_unix.go +++ b/src/pkg/exec/lp_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package exec import ( diff --git a/src/pkg/exp/gui/x11/conn.go b/src/pkg/exp/gui/x11/conn.go index 1d237816a..4645073c4 100644 --- a/src/pkg/exp/gui/x11/conn.go +++ b/src/pkg/exp/gui/x11/conn.go @@ -618,7 +618,7 @@ func NewWindowDisplay(display string) (gui.Window, os.Error) { return nil, err } - c.img = image.NewRGBA(windowWidth, windowHeight) + c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight)) c.eventc = make(chan interface{}, 16) c.flush = make(chan bool, 1) go c.readSocket() diff --git a/src/pkg/exp/norm/Makefile b/src/pkg/exp/norm/Makefile index 16239a72e..fd32f8696 100644 --- a/src/pkg/exp/norm/Makefile +++ b/src/pkg/exp/norm/Makefile @@ -25,6 +25,10 @@ maketesttables: maketesttables.go triegen.go $(GC) maketesttables.go triegen.go $(LD) -o maketesttables maketesttables.$O +normregtest: normregtest.go + $(GC) normregtest.go + $(LD) -o normregtest normregtest.$O + tables: maketables ./maketables > tables.go gofmt -w tables.go @@ -39,7 +43,10 @@ testshort: maketables maketesttables # Downloads from www.unicode.org, so not part # of standard test scripts. -test: testtables +test: testtables regtest testtables: maketables ./maketables -test -tables= + +regtest: normregtest + ./normregtest diff --git a/src/pkg/exp/norm/maketables.go b/src/pkg/exp/norm/maketables.go index dbe560618..14718c5cd 100644 --- a/src/pkg/exp/norm/maketables.go +++ b/src/pkg/exp/norm/maketables.go @@ -518,7 +518,7 @@ func completeCharFields(form int) { if JamoLBase <= i && i < JamoLEnd { f.combinesForward = true } - if JamoVBase <= i && i < JamoTEnd { + if JamoVBase <= i && i < JamoVEnd { f.quickCheck[MComposed] = QCMaybe f.combinesBackward = true f.combinesForward = true diff --git a/src/pkg/exp/norm/maketesttables.go b/src/pkg/exp/norm/maketesttables.go index c5f6a6436..fdcc114be 100644 --- a/src/pkg/exp/norm/maketesttables.go +++ b/src/pkg/exp/norm/maketesttables.go @@ -21,6 +21,7 @@ var testRunes = []int{ 0x80, 0x100, 0x7FF, // 2-byte sequences 0x800, 0x999, 0xFFFF, // 3-byte sequences 0x10000, 0x10101, 0x10FFFF, // 4-byte sequences + 0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block } const fileHeader = `// Generated by running diff --git a/src/pkg/exp/norm/normregtest.go b/src/pkg/exp/norm/normregtest.go new file mode 100644 index 000000000..3ab10b77b --- /dev/null +++ b/src/pkg/exp/norm/normregtest.go @@ -0,0 +1,295 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "bytes" + "exp/norm" + "exp/regexp" + "flag" + "fmt" + "http" + "log" + "os" + "path" + "runtime" + "strings" + "strconv" + "time" + "utf8" +) + +func main() { + flag.Parse() + loadTestData() + CharacterByCharacterTests() + StandardTests() + PerformanceTest() + if errorCount == 0 { + fmt.Println("PASS") + } +} + +const file = "NormalizationTest.txt" + +var url = flag.String("url", + "http://www.unicode.org/Public/6.0.0/ucd/"+file, + "URL of Unicode database directory") +var localFiles = flag.Bool("local", + false, + "data files have been copied to the current directory; for debugging only") + +var logger = log.New(os.Stderr, "", log.Lshortfile) + +// This regression test runs the test set in NormalizationTest.txt +// (taken from http://www.unicode.org/Public/6.0.0/ucd/). +// +// NormalizationTest.txt has form: +// @Part0 # Specific cases +// # +// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +// +// Each test has 5 columns (c1, c2, c3, c4, c5), where +// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1)) +// +// CONFORMANCE: +// 1. The following invariants must be true for all conformant implementations +// +// NFC +// c2 == NFC(c1) == NFC(c2) == NFC(c3) +// c4 == NFC(c4) == NFC(c5) +// +// NFD +// c3 == NFD(c1) == NFD(c2) == NFD(c3) +// c5 == NFD(c4) == NFD(c5) +// +// NFKC +// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) +// +// NFKD +// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) +// +// 2. For every code point X assigned in this version of Unicode that is not +// specifically listed in Part 1, the following invariants must be true +// for all conformant implementations: +// +// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) +// + +// Column types. +const ( + cRaw = iota + cNFC + cNFD + cNFKC + cNFKD + cMaxColumns +) + +// Holds data from NormalizationTest.txt +var part []Part + +type Part struct { + name string + number int + tests []Test +} + +type Test struct { + name string + partnr int + number int + rune int // used for character by character test + cols [cMaxColumns]string // Each has 5 entries, see below. +} + +func (t Test) Name() string { + if t.number < 0 { + return part[t.partnr].name + } + return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number) +} + +var partRe = regexp.MustCompile(`@Part(\d) # (.*)\n$`) +var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)\n?$`) + +var counter int + +// Load the data form NormalizationTest.txt +func loadTestData() { + if *localFiles { + pwd, _ := os.Getwd() + *url = "file://" + path.Join(pwd, file) + } + t := &http.Transport{} + t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) + c := &http.Client{Transport: t} + resp, err := c.Get(*url) + if err != nil { + logger.Fatal(err) + } + if resp.StatusCode != 200 { + logger.Fatal("bad GET status for "+file, resp.Status) + } + f := resp.Body + defer f.Close() + input := bufio.NewReader(f) + for { + line, err := input.ReadString('\n') + if err != nil { + if err == os.EOF { + break + } + logger.Fatal(err) + } + if len(line) == 0 || line[0] == '#' { + continue + } + m := partRe.FindStringSubmatch(line) + if m != nil { + if len(m) < 3 { + logger.Fatal("Failed to parse Part: ", line) + } + i, err := strconv.Atoi(m[1]) + if err != nil { + logger.Fatal(err) + } + name := m[2] + part = append(part, Part{name: name[:len(name)-1], number: i}) + continue + } + m = testRe.FindStringSubmatch(line) + if m == nil || len(m) < 7 { + logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m) + } + test := Test{name: m[6], partnr: len(part) - 1, number: counter} + counter++ + for j := 1; j < len(m)-1; j++ { + for _, split := range strings.Split(m[j], " ") { + r, err := strconv.Btoui64(split, 16) + if err != nil { + logger.Fatal(err) + } + if test.rune == 0 { + // save for CharacterByCharacterTests + test.rune = int(r) + } + var buf [utf8.UTFMax]byte + sz := utf8.EncodeRune(buf[:], int(r)) + test.cols[j-1] += string(buf[:sz]) + } + } + part := &part[len(part)-1] + part.tests = append(part.tests, test) + } +} + +var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"} + +var errorCount int + +func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) { + if gold != result { + errorCount++ + if errorCount > 20 { + return + } + st, sr, sg := []int(test), []int(result), []int(gold) + logger.Printf("%s:%s: %s(%X)=%X; want:%X: %s", + t.Name(), name, fstr[f], st, sr, sg, t.name) + } +} + +func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) { + if result != want { + errorCount++ + if errorCount > 20 { + return + } + logger.Printf("%s:%s: %s(%X)=%v; want: %v", t.Name(), name, fstr[f], []int(test), result, want) + } +} + +func doTest(t *Test, f norm.Form, gold, test string) { + result := f.Bytes([]byte(test)) + cmpResult(t, "Bytes", f, gold, test, string(result)) + for i := range test { + out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...) + cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out)) + } + cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold) +} + +func doConformanceTests(t *Test, partn int) { + for i := 0; i <= 2; i++ { + doTest(t, norm.NFC, t.cols[1], t.cols[i]) + doTest(t, norm.NFD, t.cols[2], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } + for i := 3; i <= 4; i++ { + doTest(t, norm.NFC, t.cols[3], t.cols[i]) + doTest(t, norm.NFD, t.cols[4], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } +} + +func CharacterByCharacterTests() { + tests := part[1].tests + last := 0 + for i := 0; i <= len(tests); i++ { // last one is special case + var rune int + if i == len(tests) { + rune = 0x2FA1E // Don't have to go to 0x10FFFF + } else { + rune = tests[i].rune + } + for last++; last < rune; last++ { + // Check all characters that were not explicitly listed in the test. + t := &Test{partnr: 1, number: -1} + char := string(last) + doTest(t, norm.NFC, char, char) + doTest(t, norm.NFD, char, char) + doTest(t, norm.NFKC, char, char) + doTest(t, norm.NFKD, char, char) + } + if i < len(tests) { + doConformanceTests(&tests[i], 1) + } + } +} + +func StandardTests() { + for _, j := range []int{0, 2, 3} { + for _, test := range part[j].tests { + doConformanceTests(&test, j) + } + } +} + +// PerformanceTest verifies that normalization is O(n). If any of the +// code does not properly check for maxCombiningChars, normalization +// may exhibit O(n**2) behavior. +func PerformanceTest() { + runtime.GOMAXPROCS(2) + success := make(chan bool, 1) + go func() { + buf := bytes.Repeat([]byte("\u035D"), 1024*1024) + buf = append(buf, []byte("\u035B")...) + norm.NFC.Append(nil, buf...) + success <- true + }() + timeout := time.After(1e9) + select { + case <-success: + // test completed before the timeout + case <-timeout: + errorCount++ + logger.Printf(`unexpectedly long time to complete PerformanceTest`) + } +} diff --git a/src/pkg/exp/norm/tables.go b/src/pkg/exp/norm/tables.go index 36dcd5c84..55ff052dc 100644 --- a/src/pkg/exp/norm/tables.go +++ b/src/pkg/exp/norm/tables.go @@ -2490,545 +2490,632 @@ var decomps = [...]byte{ 0x98, 0x80, } -// nfcDecompValues: 4992 entries, 9984 bytes +// nfcDecompValues: 1408 entries, 2816 bytes // Block 2 is the null block. -var nfcDecompValues = [4992]uint16{ +var nfcDecompValues = [1408]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00c0: 0x0032, 0x00c1: 0x0036, 0x00c2: 0x003a, 0x00c3: 0x003e, 0x00c4: 0x0042, 0x00c5: 0x0046, - 0x00c7: 0x004a, 0x00c8: 0x004e, 0x00c9: 0x0052, 0x00ca: 0x0056, 0x00cb: 0x005a, - 0x00cc: 0x005e, 0x00cd: 0x0062, 0x00ce: 0x0066, 0x00cf: 0x006a, 0x00d1: 0x006e, - 0x00d2: 0x0072, 0x00d3: 0x0076, 0x00d4: 0x007a, 0x00d5: 0x007e, 0x00d6: 0x0082, - 0x00d9: 0x0086, 0x00da: 0x008a, 0x00db: 0x008e, 0x00dc: 0x0092, 0x00dd: 0x0096, - 0x00e0: 0x009a, 0x00e1: 0x009e, 0x00e2: 0x00a2, 0x00e3: 0x00a6, - 0x00e4: 0x00aa, 0x00e5: 0x00ae, 0x00e7: 0x00b2, 0x00e8: 0x00b6, 0x00e9: 0x00ba, - 0x00ea: 0x00be, 0x00eb: 0x00c2, 0x00ec: 0x00c6, 0x00ed: 0x00ca, 0x00ee: 0x00ce, 0x00ef: 0x00d2, - 0x00f1: 0x00d6, 0x00f2: 0x00da, 0x00f3: 0x00de, 0x00f4: 0x00e2, 0x00f5: 0x00e6, - 0x00f6: 0x00ea, 0x00f9: 0x00ee, 0x00fa: 0x00f2, 0x00fb: 0x00f6, - 0x00fc: 0x00fa, 0x00fd: 0x00fe, 0x00ff: 0x0102, + 0x00cd: 0x02fb, 0x00ce: 0x02ff, 0x00cf: 0x0303, 0x00d0: 0x0307, 0x00d1: 0x030b, + 0x00d2: 0x030f, 0x00d3: 0x0313, 0x00d4: 0x0317, 0x00d5: 0x031b, 0x00d6: 0x0321, 0x00d7: 0x0327, + 0x00d8: 0x032d, 0x00d9: 0x0333, 0x00da: 0x0339, 0x00db: 0x033f, 0x00dc: 0x0345, + 0x00de: 0x034b, 0x00df: 0x0351, 0x00e0: 0x0357, 0x00e1: 0x035d, 0x00e2: 0x0363, 0x00e3: 0x0368, + 0x00e6: 0x036d, 0x00e7: 0x0371, 0x00e8: 0x0375, 0x00e9: 0x0379, + 0x00ea: 0x037d, 0x00eb: 0x0381, 0x00ec: 0x0385, 0x00ed: 0x038b, 0x00ee: 0x0391, 0x00ef: 0x0396, + 0x00f0: 0x039b, 0x00f4: 0x03a8, 0x00f5: 0x03ac, + 0x00f8: 0x03b0, 0x00f9: 0x03b4, 0x00fa: 0x03b8, 0x00fb: 0x03be, + 0x00fc: 0x03c4, 0x00fd: 0x03c9, 0x00fe: 0x03ce, 0x00ff: 0x03d3, // Block 0x4, offset 0x100 - 0x0100: 0x0106, 0x0101: 0x010a, 0x0102: 0x010e, 0x0103: 0x0112, 0x0104: 0x0116, 0x0105: 0x011a, - 0x0106: 0x011e, 0x0107: 0x0122, 0x0108: 0x0126, 0x0109: 0x012a, 0x010a: 0x012e, 0x010b: 0x0132, - 0x010c: 0x0136, 0x010d: 0x013a, 0x010e: 0x013e, 0x010f: 0x0142, - 0x0112: 0x0146, 0x0113: 0x014a, 0x0114: 0x014e, 0x0115: 0x0152, 0x0116: 0x0156, 0x0117: 0x015a, - 0x0118: 0x015e, 0x0119: 0x0162, 0x011a: 0x0166, 0x011b: 0x016a, 0x011c: 0x016e, 0x011d: 0x0172, - 0x011e: 0x0176, 0x011f: 0x017a, 0x0120: 0x017e, 0x0121: 0x0182, 0x0122: 0x0186, 0x0123: 0x018a, - 0x0124: 0x018e, 0x0125: 0x0192, 0x0128: 0x0196, 0x0129: 0x019a, - 0x012a: 0x019e, 0x012b: 0x01a2, 0x012c: 0x01a6, 0x012d: 0x01aa, 0x012e: 0x01ae, 0x012f: 0x01b2, - 0x0130: 0x01b6, 0x0134: 0x01c0, 0x0135: 0x01c4, - 0x0136: 0x01c8, 0x0137: 0x01cc, 0x0139: 0x01d0, 0x013a: 0x01d4, 0x013b: 0x01d8, - 0x013c: 0x01dc, 0x013d: 0x01e0, 0x013e: 0x01e4, + 0x0100: 0x0b02, 0x0101: 0x0b06, 0x0102: 0x0b0a, 0x0103: 0x0b0e, 0x0104: 0x0b12, 0x0105: 0x0b16, + 0x0106: 0x0b1a, 0x0107: 0x0b1e, 0x0108: 0x0b22, 0x0109: 0x0b26, 0x010a: 0x0b2a, 0x010b: 0x0b2e, + 0x010c: 0x0b32, 0x010d: 0x0b38, 0x010e: 0x0b3e, 0x010f: 0x0b44, 0x0110: 0x0b4a, 0x0111: 0x0b50, + 0x0112: 0x0b56, 0x0113: 0x0b5c, 0x0114: 0x0b62, 0x0115: 0x0b66, 0x0116: 0x0b6a, 0x0117: 0x0b6e, + 0x0118: 0x0b72, 0x0119: 0x0b76, 0x011a: 0x0b7a, 0x011b: 0x0b7e, 0x011c: 0x0b82, 0x011d: 0x0b88, + 0x011e: 0x0b8e, 0x011f: 0x0b92, 0x0120: 0x0b96, 0x0121: 0x0b9a, 0x0122: 0x0b9e, 0x0123: 0x0ba2, + 0x0124: 0x0ba6, 0x0125: 0x0bac, 0x0126: 0x0bb2, 0x0127: 0x0bb8, 0x0128: 0x0bbe, 0x0129: 0x0bc4, + 0x012a: 0x0bca, 0x012b: 0x0bce, 0x012c: 0x0bd2, 0x012d: 0x0bd6, 0x012e: 0x0bda, 0x012f: 0x0bde, + 0x0130: 0x0be2, 0x0131: 0x0be6, 0x0132: 0x0bea, 0x0133: 0x0bee, 0x0134: 0x0bf2, 0x0135: 0x0bf6, + 0x0136: 0x0bfa, 0x0137: 0x0bfe, 0x0138: 0x0c02, 0x0139: 0x0c08, 0x013a: 0x0c0e, 0x013b: 0x0c14, + 0x013c: 0x0c1a, 0x013d: 0x0c1e, 0x013e: 0x0c22, 0x013f: 0x0c26, // Block 0x5, offset 0x140 - 0x0143: 0x01f0, 0x0144: 0x01f4, 0x0145: 0x01f8, - 0x0146: 0x01fc, 0x0147: 0x0200, 0x0148: 0x0204, - 0x014c: 0x020c, 0x014d: 0x0210, 0x014e: 0x0214, 0x014f: 0x0218, 0x0150: 0x021c, 0x0151: 0x0220, - 0x0154: 0x0224, 0x0155: 0x0228, 0x0156: 0x022c, 0x0157: 0x0230, - 0x0158: 0x0234, 0x0159: 0x0238, 0x015a: 0x023c, 0x015b: 0x0240, 0x015c: 0x0244, 0x015d: 0x0248, - 0x015e: 0x024c, 0x015f: 0x0250, 0x0160: 0x0254, 0x0161: 0x0258, 0x0162: 0x025c, 0x0163: 0x0260, - 0x0164: 0x0264, 0x0165: 0x0268, 0x0168: 0x026c, 0x0169: 0x0270, - 0x016a: 0x0274, 0x016b: 0x0278, 0x016c: 0x027c, 0x016d: 0x0280, 0x016e: 0x0284, 0x016f: 0x0288, - 0x0170: 0x028c, 0x0171: 0x0290, 0x0172: 0x0294, 0x0173: 0x0298, 0x0174: 0x029c, 0x0175: 0x02a0, - 0x0176: 0x02a4, 0x0177: 0x02a8, 0x0178: 0x02ac, 0x0179: 0x02b0, 0x017a: 0x02b4, 0x017b: 0x02b8, - 0x017c: 0x02bc, 0x017d: 0x02c0, 0x017e: 0x02c4, + 0x0140: 0x0c2a, 0x0141: 0x0c2e, 0x0142: 0x0c32, 0x0143: 0x0c36, 0x0144: 0x0c3a, 0x0145: 0x0c3e, + 0x0146: 0x0c42, 0x0147: 0x0c46, 0x0148: 0x0c4a, 0x0149: 0x0c4e, 0x014a: 0x0c52, 0x014b: 0x0c56, + 0x014c: 0x0c5a, 0x014d: 0x0c5e, 0x014e: 0x0c62, 0x014f: 0x0c66, 0x0150: 0x0c6a, 0x0151: 0x0c6e, + 0x0152: 0x0c72, 0x0153: 0x0c76, 0x0154: 0x0c7a, 0x0155: 0x0c7e, 0x0156: 0x0c82, 0x0157: 0x0c86, + 0x0158: 0x0c8a, 0x0159: 0x0c8e, 0x015b: 0x0c96, + 0x0160: 0x0c9b, 0x0161: 0x0c9f, 0x0162: 0x0ca3, 0x0163: 0x0ca7, + 0x0164: 0x0cab, 0x0165: 0x0cb1, 0x0166: 0x0cb7, 0x0167: 0x0cbd, 0x0168: 0x0cc3, 0x0169: 0x0cc9, + 0x016a: 0x0ccf, 0x016b: 0x0cd5, 0x016c: 0x0cdb, 0x016d: 0x0ce1, 0x016e: 0x0ce7, 0x016f: 0x0ced, + 0x0170: 0x0cf3, 0x0171: 0x0cf9, 0x0172: 0x0cff, 0x0173: 0x0d05, 0x0174: 0x0d0b, 0x0175: 0x0d11, + 0x0176: 0x0d17, 0x0177: 0x0d1d, 0x0178: 0x0d23, 0x0179: 0x0d27, 0x017a: 0x0d2b, 0x017b: 0x0d2f, + 0x017c: 0x0d33, 0x017d: 0x0d37, 0x017e: 0x0d3b, 0x017f: 0x0d41, // Block 0x6, offset 0x180 - 0x01a0: 0x02ca, 0x01a1: 0x02ce, - 0x01af: 0x02d2, - 0x01b0: 0x02d6, + 0x0180: 0x0d47, 0x0181: 0x0d4d, 0x0182: 0x0d53, 0x0183: 0x0d59, 0x0184: 0x0d5f, 0x0185: 0x0d65, + 0x0186: 0x0d6b, 0x0187: 0x0d71, 0x0188: 0x0d77, 0x0189: 0x0d7b, 0x018a: 0x0d7f, 0x018b: 0x0d83, + 0x018c: 0x0d87, 0x018d: 0x0d8b, 0x018e: 0x0d8f, 0x018f: 0x0d93, 0x0190: 0x0d97, 0x0191: 0x0d9d, + 0x0192: 0x0da3, 0x0193: 0x0da9, 0x0194: 0x0daf, 0x0195: 0x0db5, 0x0196: 0x0dbb, 0x0197: 0x0dc1, + 0x0198: 0x0dc7, 0x0199: 0x0dcd, 0x019a: 0x0dd3, 0x019b: 0x0dd9, 0x019c: 0x0ddf, 0x019d: 0x0de5, + 0x019e: 0x0deb, 0x019f: 0x0df1, 0x01a0: 0x0df7, 0x01a1: 0x0dfd, 0x01a2: 0x0e03, 0x01a3: 0x0e09, + 0x01a4: 0x0e0f, 0x01a5: 0x0e13, 0x01a6: 0x0e17, 0x01a7: 0x0e1b, 0x01a8: 0x0e1f, 0x01a9: 0x0e25, + 0x01aa: 0x0e2b, 0x01ab: 0x0e31, 0x01ac: 0x0e37, 0x01ad: 0x0e3d, 0x01ae: 0x0e43, 0x01af: 0x0e49, + 0x01b0: 0x0e4f, 0x01b1: 0x0e55, 0x01b2: 0x0e5b, 0x01b3: 0x0e5f, 0x01b4: 0x0e63, 0x01b5: 0x0e67, + 0x01b6: 0x0e6b, 0x01b7: 0x0e6f, 0x01b8: 0x0e73, 0x01b9: 0x0e77, // Block 0x7, offset 0x1c0 - 0x01cd: 0x02fb, 0x01ce: 0x02ff, 0x01cf: 0x0303, 0x01d0: 0x0307, 0x01d1: 0x030b, - 0x01d2: 0x030f, 0x01d3: 0x0313, 0x01d4: 0x0317, 0x01d5: 0x031b, 0x01d6: 0x0321, 0x01d7: 0x0327, - 0x01d8: 0x032d, 0x01d9: 0x0333, 0x01da: 0x0339, 0x01db: 0x033f, 0x01dc: 0x0345, - 0x01de: 0x034b, 0x01df: 0x0351, 0x01e0: 0x0357, 0x01e1: 0x035d, 0x01e2: 0x0363, 0x01e3: 0x0368, - 0x01e6: 0x036d, 0x01e7: 0x0371, 0x01e8: 0x0375, 0x01e9: 0x0379, - 0x01ea: 0x037d, 0x01eb: 0x0381, 0x01ec: 0x0385, 0x01ed: 0x038b, 0x01ee: 0x0391, 0x01ef: 0x0396, - 0x01f0: 0x039b, 0x01f4: 0x03a8, 0x01f5: 0x03ac, - 0x01f8: 0x03b0, 0x01f9: 0x03b4, 0x01fa: 0x03b8, 0x01fb: 0x03be, - 0x01fc: 0x03c4, 0x01fd: 0x03c9, 0x01fe: 0x03ce, 0x01ff: 0x03d3, + 0x01c0: 0x0e7b, 0x01c1: 0x0e80, 0x01c2: 0x0e85, 0x01c3: 0x0e8c, 0x01c4: 0x0e93, 0x01c5: 0x0e9a, + 0x01c6: 0x0ea1, 0x01c7: 0x0ea8, 0x01c8: 0x0eaf, 0x01c9: 0x0eb4, 0x01ca: 0x0eb9, 0x01cb: 0x0ec0, + 0x01cc: 0x0ec7, 0x01cd: 0x0ece, 0x01ce: 0x0ed5, 0x01cf: 0x0edc, 0x01d0: 0x0ee3, 0x01d1: 0x0ee8, + 0x01d2: 0x0eed, 0x01d3: 0x0ef4, 0x01d4: 0x0efb, 0x01d5: 0x0f02, + 0x01d8: 0x0f09, 0x01d9: 0x0f0e, 0x01da: 0x0f13, 0x01db: 0x0f1a, 0x01dc: 0x0f21, 0x01dd: 0x0f28, + 0x01e0: 0x0f2f, 0x01e1: 0x0f34, 0x01e2: 0x0f39, 0x01e3: 0x0f40, + 0x01e4: 0x0f47, 0x01e5: 0x0f4e, 0x01e6: 0x0f55, 0x01e7: 0x0f5c, 0x01e8: 0x0f63, 0x01e9: 0x0f68, + 0x01ea: 0x0f6d, 0x01eb: 0x0f74, 0x01ec: 0x0f7b, 0x01ed: 0x0f82, 0x01ee: 0x0f89, 0x01ef: 0x0f90, + 0x01f0: 0x0f97, 0x01f1: 0x0f9c, 0x01f2: 0x0fa1, 0x01f3: 0x0fa8, 0x01f4: 0x0faf, 0x01f5: 0x0fb6, + 0x01f6: 0x0fbd, 0x01f7: 0x0fc4, 0x01f8: 0x0fcb, 0x01f9: 0x0fd0, 0x01fa: 0x0fd5, 0x01fb: 0x0fdc, + 0x01fc: 0x0fe3, 0x01fd: 0x0fea, 0x01fe: 0x0ff1, 0x01ff: 0x0ff8, // Block 0x8, offset 0x200 - 0x0200: 0x03d8, 0x0201: 0x03dc, 0x0202: 0x03e0, 0x0203: 0x03e4, 0x0204: 0x03e8, 0x0205: 0x03ec, - 0x0206: 0x03f0, 0x0207: 0x03f4, 0x0208: 0x03f8, 0x0209: 0x03fc, 0x020a: 0x0400, 0x020b: 0x0404, - 0x020c: 0x0408, 0x020d: 0x040c, 0x020e: 0x0410, 0x020f: 0x0414, 0x0210: 0x0418, 0x0211: 0x041c, - 0x0212: 0x0420, 0x0213: 0x0424, 0x0214: 0x0428, 0x0215: 0x042c, 0x0216: 0x0430, 0x0217: 0x0434, - 0x0218: 0x0438, 0x0219: 0x043c, 0x021a: 0x0440, 0x021b: 0x0444, - 0x021e: 0x0448, 0x021f: 0x044c, - 0x0226: 0x0450, 0x0227: 0x0454, 0x0228: 0x0458, 0x0229: 0x045c, - 0x022a: 0x0460, 0x022b: 0x0466, 0x022c: 0x046c, 0x022d: 0x0472, 0x022e: 0x0478, 0x022f: 0x047c, - 0x0230: 0x0480, 0x0231: 0x0486, 0x0232: 0x048c, 0x0233: 0x0490, + 0x0200: 0x0fff, 0x0201: 0x1004, 0x0202: 0x1009, 0x0203: 0x1010, 0x0204: 0x1017, 0x0205: 0x101e, + 0x0208: 0x1025, 0x0209: 0x102a, 0x020a: 0x102f, 0x020b: 0x1036, + 0x020c: 0x103d, 0x020d: 0x1044, 0x0210: 0x104b, 0x0211: 0x1050, + 0x0212: 0x1055, 0x0213: 0x105c, 0x0214: 0x1063, 0x0215: 0x106a, 0x0216: 0x1071, 0x0217: 0x1078, + 0x0219: 0x107f, 0x021b: 0x1084, 0x021d: 0x108b, + 0x021f: 0x1092, 0x0220: 0x1099, 0x0221: 0x109e, 0x0222: 0x10a3, 0x0223: 0x10aa, + 0x0224: 0x10b1, 0x0225: 0x10b8, 0x0226: 0x10bf, 0x0227: 0x10c6, 0x0228: 0x10cd, 0x0229: 0x10d2, + 0x022a: 0x10d7, 0x022b: 0x10de, 0x022c: 0x10e5, 0x022d: 0x10ec, 0x022e: 0x10f3, 0x022f: 0x10fa, + 0x0230: 0x1101, 0x0231: 0x0525, 0x0232: 0x1106, 0x0233: 0x052a, 0x0234: 0x110b, 0x0235: 0x052f, + 0x0236: 0x1110, 0x0237: 0x0534, 0x0238: 0x1115, 0x0239: 0x054a, 0x023a: 0x111a, 0x023b: 0x054f, + 0x023c: 0x111f, 0x023d: 0x0554, // Block 0x9, offset 0x240 - 0x0240: 0x04cc, 0x0241: 0x04cf, 0x0243: 0x04d2, 0x0244: 0x04d5, - 0x0274: 0x04da, - 0x027e: 0x04e1, + 0x0240: 0x1124, 0x0241: 0x112b, 0x0242: 0x1132, 0x0243: 0x113b, 0x0244: 0x1144, 0x0245: 0x114d, + 0x0246: 0x1156, 0x0247: 0x115f, 0x0248: 0x1168, 0x0249: 0x116f, 0x024a: 0x1176, 0x024b: 0x117f, + 0x024c: 0x1188, 0x024d: 0x1191, 0x024e: 0x119a, 0x024f: 0x11a3, 0x0250: 0x11ac, 0x0251: 0x11b3, + 0x0252: 0x11ba, 0x0253: 0x11c3, 0x0254: 0x11cc, 0x0255: 0x11d5, 0x0256: 0x11de, 0x0257: 0x11e7, + 0x0258: 0x11f0, 0x0259: 0x11f7, 0x025a: 0x11fe, 0x025b: 0x1207, 0x025c: 0x1210, 0x025d: 0x1219, + 0x025e: 0x1222, 0x025f: 0x122b, 0x0260: 0x1234, 0x0261: 0x123b, 0x0262: 0x1242, 0x0263: 0x124b, + 0x0264: 0x1254, 0x0265: 0x125d, 0x0266: 0x1266, 0x0267: 0x126f, 0x0268: 0x1278, 0x0269: 0x127f, + 0x026a: 0x1286, 0x026b: 0x128f, 0x026c: 0x1298, 0x026d: 0x12a1, 0x026e: 0x12aa, 0x026f: 0x12b3, + 0x0270: 0x12bc, 0x0271: 0x12c1, 0x0272: 0x12c6, 0x0273: 0x12cd, 0x0274: 0x12d2, + 0x0276: 0x12d9, 0x0277: 0x12de, 0x0278: 0x12e5, 0x0279: 0x12ea, 0x027a: 0x12ef, 0x027b: 0x04ee, + 0x027c: 0x12f4, 0x027e: 0x12fd, // Block 0xa, offset 0x280 - 0x0285: 0x04e3, - 0x0286: 0x04ee, 0x0287: 0x04f3, 0x0288: 0x04f6, 0x0289: 0x04fb, 0x028a: 0x0500, - 0x028c: 0x0505, 0x028e: 0x050a, 0x028f: 0x050f, 0x0290: 0x0514, - 0x02aa: 0x051b, 0x02ab: 0x0520, 0x02ac: 0x0525, 0x02ad: 0x052a, 0x02ae: 0x052f, 0x02af: 0x0534, - 0x02b0: 0x0539, + 0x0281: 0x1304, 0x0282: 0x130f, 0x0283: 0x1316, 0x0284: 0x131b, + 0x0286: 0x1322, 0x0287: 0x1327, 0x0288: 0x132e, 0x0289: 0x04f6, 0x028a: 0x1333, 0x028b: 0x04fb, + 0x028c: 0x1338, 0x028d: 0x133d, 0x028e: 0x1349, 0x028f: 0x1355, 0x0290: 0x1361, 0x0291: 0x1366, + 0x0292: 0x136b, 0x0293: 0x0514, 0x0296: 0x1372, 0x0297: 0x1377, + 0x0298: 0x137e, 0x0299: 0x1383, 0x029a: 0x1388, 0x029b: 0x0500, 0x029d: 0x138d, + 0x029e: 0x1399, 0x029f: 0x13a5, 0x02a0: 0x13b1, 0x02a1: 0x13b6, 0x02a2: 0x13bb, 0x02a3: 0x0539, + 0x02a4: 0x13c2, 0x02a5: 0x13c7, 0x02a6: 0x13cc, 0x02a7: 0x13d1, 0x02a8: 0x13d8, 0x02a9: 0x13dd, + 0x02aa: 0x13e2, 0x02ab: 0x050a, 0x02ac: 0x13e7, 0x02ad: 0x13ec, 0x02ae: 0x04e3, 0x02af: 0x13f7, + 0x02b2: 0x13f9, 0x02b3: 0x1400, 0x02b4: 0x1405, + 0x02b6: 0x140c, 0x02b7: 0x1411, 0x02b8: 0x1418, 0x02b9: 0x0505, 0x02ba: 0x141d, 0x02bb: 0x050f, + 0x02bc: 0x1422, 0x02bd: 0x1427, // Block 0xb, offset 0x2c0 - 0x02ca: 0x0540, 0x02cb: 0x0545, - 0x02cc: 0x054a, 0x02cd: 0x054f, 0x02ce: 0x0554, - 0x02d3: 0x0562, 0x02d4: 0x0567, + 0x02cc: 0x1b8a, 0x02ce: 0x1b91, 0x02d0: 0x1b98, + 0x02d2: 0x1b9f, 0x02d4: 0x1ba6, 0x02d6: 0x1bad, + 0x02d8: 0x1bb4, 0x02da: 0x1bbb, 0x02dc: 0x1bc2, + 0x02de: 0x1bc9, 0x02e0: 0x1bd0, 0x02e2: 0x1bd7, + 0x02e5: 0x1bde, 0x02e7: 0x1be5, 0x02e9: 0x1bec, + 0x02f0: 0x1bf3, 0x02f1: 0x1bfa, 0x02f3: 0x1c01, 0x02f4: 0x1c08, + 0x02f6: 0x1c0f, 0x02f7: 0x1c16, 0x02f9: 0x1c1d, 0x02fa: 0x1c24, + 0x02fc: 0x1c2b, 0x02fd: 0x1c32, // Block 0xc, offset 0x300 - 0x0300: 0x0584, 0x0301: 0x0589, 0x0303: 0x058e, - 0x0307: 0x0593, - 0x030c: 0x0598, 0x030d: 0x059d, 0x030e: 0x05a2, - 0x0319: 0x05a7, - 0x0339: 0x05ac, + 0x0300: 0x2fce, 0x0301: 0x2fd2, 0x0302: 0x2fd6, 0x0303: 0x2fda, 0x0304: 0x2fde, 0x0305: 0x2fe2, + 0x0306: 0x2fe6, 0x0307: 0x2fea, 0x0308: 0x2fee, 0x0309: 0x2eed, 0x030a: 0x2ff2, 0x030b: 0x2ef1, + 0x030c: 0x2ff6, 0x030d: 0x2ffa, 0x030e: 0x2ffe, 0x030f: 0x3002, 0x0310: 0x3006, 0x0311: 0x2e6d, + 0x0312: 0x2b15, 0x0313: 0x300a, 0x0314: 0x300e, 0x0315: 0x195a, 0x0316: 0x2c25, 0x0317: 0x2d71, + 0x0318: 0x3012, 0x0319: 0x3016, 0x031a: 0x2f0d, 0x031b: 0x301a, 0x031c: 0x2f11, 0x031d: 0x301e, + 0x031e: 0x3022, 0x031f: 0x3026, 0x0320: 0x2e75, 0x0321: 0x302a, 0x0322: 0x302e, 0x0323: 0x3032, + 0x0324: 0x3036, 0x0325: 0x303a, 0x0326: 0x2e79, 0x0327: 0x303e, 0x0328: 0x3042, 0x0329: 0x3046, + 0x032a: 0x304a, 0x032b: 0x304e, 0x032c: 0x3052, 0x032d: 0x2f41, 0x032e: 0x3056, 0x032f: 0x305a, + 0x0330: 0x2cb1, 0x0331: 0x305e, 0x0332: 0x2f51, 0x0333: 0x3062, 0x0334: 0x3066, 0x0335: 0x306a, + 0x0336: 0x306e, 0x0337: 0x3072, 0x0338: 0x2f65, 0x0339: 0x3076, 0x033a: 0x2e99, 0x033b: 0x307a, + 0x033c: 0x2f69, 0x033d: 0x2bd9, 0x033e: 0x307e, 0x033f: 0x2f6d, // Block 0xd, offset 0x340 - 0x0350: 0x05b1, 0x0351: 0x05b6, - 0x0353: 0x05bb, 0x0357: 0x05c0, - 0x035c: 0x05c5, 0x035d: 0x05ca, - 0x035e: 0x05cf, - 0x0376: 0x05d4, 0x0377: 0x05d9, + 0x0340: 0x3082, 0x0341: 0x2f75, 0x0342: 0x3086, 0x0343: 0x308a, 0x0344: 0x308e, 0x0345: 0x3092, + 0x0346: 0x3096, 0x0347: 0x2f7d, 0x0348: 0x2e8d, 0x0349: 0x309a, 0x034a: 0x2f81, 0x034b: 0x309e, + 0x034c: 0x2f85, 0x034d: 0x30a2, 0x034e: 0x1b76, 0x034f: 0x30a6, 0x0350: 0x30ab, 0x0351: 0x30b0, + 0x0352: 0x30b5, 0x0353: 0x30b9, 0x0354: 0x30bd, 0x0355: 0x30c1, 0x0356: 0x30c6, 0x0357: 0x30cb, + 0x0358: 0x30d0, 0x0359: 0x30d4, // Block 0xe, offset 0x380 - 0x0381: 0x05de, 0x0382: 0x05e3, - 0x0390: 0x05e8, 0x0391: 0x05ed, - 0x0392: 0x05f2, 0x0393: 0x05f7, 0x0396: 0x05fc, 0x0397: 0x0601, - 0x039a: 0x0606, 0x039b: 0x060b, 0x039c: 0x0610, 0x039d: 0x0615, - 0x039e: 0x061a, 0x039f: 0x061f, 0x03a2: 0x0624, 0x03a3: 0x0629, - 0x03a4: 0x062e, 0x03a5: 0x0633, 0x03a6: 0x0638, 0x03a7: 0x063d, - 0x03aa: 0x0642, 0x03ab: 0x0647, 0x03ac: 0x064c, 0x03ad: 0x0651, 0x03ae: 0x0656, 0x03af: 0x065b, - 0x03b0: 0x0660, 0x03b1: 0x0665, 0x03b2: 0x066a, 0x03b3: 0x066f, 0x03b4: 0x0674, 0x03b5: 0x0679, - 0x03b8: 0x067e, 0x03b9: 0x0683, + 0x0380: 0x3d23, 0x0381: 0x3d27, 0x0382: 0x3d2b, 0x0383: 0x3d2f, 0x0384: 0x3d34, 0x0385: 0x2eb5, + 0x0386: 0x3d38, 0x0387: 0x3d3c, 0x0388: 0x3d40, 0x0389: 0x3d44, 0x038a: 0x2eb9, 0x038b: 0x3d48, + 0x038c: 0x3d4c, 0x038d: 0x3d50, 0x038e: 0x2ebd, 0x038f: 0x3d55, 0x0390: 0x3d59, 0x0391: 0x3d5d, + 0x0392: 0x3d61, 0x0393: 0x3d66, 0x0394: 0x3d6a, 0x0395: 0x3c71, 0x0396: 0x3d6e, 0x0397: 0x3d73, + 0x0398: 0x3d77, 0x0399: 0x3d7b, 0x039a: 0x3d7f, 0x039b: 0x2f9a, 0x039c: 0x3d83, 0x039d: 0x1866, + 0x039e: 0x3d88, 0x039f: 0x3d8c, 0x03a0: 0x3d90, 0x03a1: 0x3d94, 0x03a2: 0x3cb9, 0x03a3: 0x3d98, + 0x03a4: 0x3d9c, 0x03a5: 0x2fae, 0x03a6: 0x2ec1, 0x03a7: 0x2ec5, 0x03a8: 0x2fb2, 0x03a9: 0x3da0, + 0x03aa: 0x3da4, 0x03ab: 0x2bf1, 0x03ac: 0x3da8, 0x03ad: 0x2ec9, 0x03ae: 0x3dac, 0x03af: 0x3db0, + 0x03b0: 0x3db4, 0x03b1: 0x3db8, 0x03b2: 0x3db8, 0x03b3: 0x3db8, 0x03b4: 0x3dbc, 0x03b5: 0x3dc1, + 0x03b6: 0x3dc5, 0x03b7: 0x3dc9, 0x03b8: 0x3dcd, 0x03b9: 0x3dd2, 0x03ba: 0x3dd6, 0x03bb: 0x3dda, + 0x03bc: 0x3dde, 0x03bd: 0x3de2, 0x03be: 0x3de6, 0x03bf: 0x3dea, // Block 0xf, offset 0x3c0 - 0x03e2: 0x068d, 0x03e3: 0x0692, - 0x03e4: 0x0697, 0x03e5: 0x069c, 0x03e6: 0x06a1, + 0x03c0: 0x3dee, 0x03c1: 0x3df2, 0x03c2: 0x3df6, 0x03c3: 0x3dfa, 0x03c4: 0x3dfe, 0x03c5: 0x3e02, + 0x03c6: 0x3e02, 0x03c7: 0x2fba, 0x03c8: 0x3e06, 0x03c9: 0x3e0a, 0x03ca: 0x3e0e, 0x03cb: 0x3e12, + 0x03cc: 0x2ed1, 0x03cd: 0x3e16, 0x03ce: 0x3e1a, 0x03cf: 0x3e1e, 0x03d0: 0x2e39, 0x03d1: 0x3e22, + 0x03d2: 0x3e26, 0x03d3: 0x3e2a, 0x03d4: 0x3e2e, 0x03d5: 0x3e32, 0x03d6: 0x3e36, 0x03d7: 0x3e3a, + 0x03d8: 0x3e3e, 0x03d9: 0x3e42, 0x03da: 0x3e47, 0x03db: 0x3e4b, 0x03dc: 0x3e4f, 0x03dd: 0x3c55, + 0x03de: 0x3e53, 0x03df: 0x3e57, 0x03e0: 0x3e5b, 0x03e1: 0x3e60, 0x03e2: 0x3e65, 0x03e3: 0x3e69, + 0x03e4: 0x3e6d, 0x03e5: 0x3e71, 0x03e6: 0x3e75, 0x03e7: 0x3e79, 0x03e8: 0x3e7d, 0x03e9: 0x3e81, + 0x03ea: 0x3e85, 0x03eb: 0x3e85, 0x03ec: 0x3e89, 0x03ed: 0x3e8e, 0x03ee: 0x3e92, 0x03ef: 0x2be1, + 0x03f0: 0x3e96, 0x03f1: 0x3e9a, 0x03f2: 0x3e9f, 0x03f3: 0x3ea3, 0x03f4: 0x3ea7, 0x03f5: 0x18ce, + 0x03f6: 0x3eab, 0x03f7: 0x3eaf, 0x03f8: 0x18d6, 0x03f9: 0x3eb3, 0x03fa: 0x3eb7, 0x03fb: 0x3ebb, + 0x03fc: 0x3ec0, 0x03fd: 0x3ec4, 0x03fe: 0x3ec9, 0x03ff: 0x3ecd, // Block 0x10, offset 0x400 - 0x0400: 0x06ba, 0x0402: 0x06bf, - 0x0413: 0x06c4, + 0x0400: 0x3ed1, 0x0401: 0x3ed5, 0x0402: 0x3ed9, 0x0403: 0x3edd, 0x0404: 0x3ee1, 0x0405: 0x3ee5, + 0x0406: 0x3ee9, 0x0407: 0x3eed, 0x0408: 0x3ef1, 0x0409: 0x3ef5, 0x040a: 0x3efa, 0x040b: 0x3efe, + 0x040c: 0x3f02, 0x040d: 0x3f06, 0x040e: 0x2b11, 0x040f: 0x3f0a, 0x0410: 0x18fe, 0x0411: 0x3f0f, + 0x0412: 0x3f0f, 0x0413: 0x3f14, 0x0414: 0x3f18, 0x0415: 0x3f18, 0x0416: 0x3f1c, 0x0417: 0x3f20, + 0x0418: 0x3f25, 0x0419: 0x3f2a, 0x041a: 0x3f2e, 0x041b: 0x3f32, 0x041c: 0x3f36, 0x041d: 0x3f3a, + 0x041e: 0x3f3e, 0x041f: 0x3f42, 0x0420: 0x3f46, 0x0421: 0x3f4a, 0x0422: 0x3f4e, 0x0423: 0x2ee5, + 0x0424: 0x3f52, 0x0425: 0x3f57, 0x0426: 0x3f5b, 0x0427: 0x3f5f, 0x0428: 0x2fea, 0x0429: 0x3f5f, + 0x042a: 0x3f63, 0x042b: 0x2eed, 0x042c: 0x3f67, 0x042d: 0x3f6b, 0x042e: 0x3f6f, 0x042f: 0x3f73, + 0x0430: 0x2ef1, 0x0431: 0x2aa5, 0x0432: 0x3f77, 0x0433: 0x3f7b, 0x0434: 0x3f7f, 0x0435: 0x3f83, + 0x0436: 0x3f87, 0x0437: 0x3f8b, 0x0438: 0x3f8f, 0x0439: 0x3f94, 0x043a: 0x3f98, 0x043b: 0x3f9c, + 0x043c: 0x3fa0, 0x043d: 0x3fa4, 0x043e: 0x3fa8, 0x043f: 0x3fad, // Block 0x11, offset 0x440 - 0x0469: 0x06c9, - 0x0471: 0x06d0, 0x0474: 0x06d7, + 0x0440: 0x3fb1, 0x0441: 0x3fb5, 0x0442: 0x3fb9, 0x0443: 0x3fbd, 0x0444: 0x3fc1, 0x0445: 0x3fc5, + 0x0446: 0x3fc9, 0x0447: 0x3fcd, 0x0448: 0x2ef5, 0x0449: 0x3fd1, 0x044a: 0x3fd5, 0x044b: 0x3fda, + 0x044c: 0x3fde, 0x044d: 0x3fe2, 0x044e: 0x3fe6, 0x044f: 0x2efd, 0x0450: 0x3fea, 0x0451: 0x3fee, + 0x0452: 0x3ff2, 0x0453: 0x3ff6, 0x0454: 0x3ffa, 0x0455: 0x3ffe, 0x0456: 0x4002, 0x0457: 0x4006, + 0x0458: 0x2b15, 0x0459: 0x300a, 0x045a: 0x400a, 0x045b: 0x400e, 0x045c: 0x4012, 0x045d: 0x4016, + 0x045e: 0x401b, 0x045f: 0x401f, 0x0460: 0x4023, 0x0461: 0x4027, 0x0462: 0x2f01, 0x0463: 0x402b, + 0x0464: 0x4030, 0x0465: 0x4034, 0x0466: 0x4038, 0x0467: 0x30b5, 0x0468: 0x403c, 0x0469: 0x4040, + 0x046a: 0x4044, 0x046b: 0x4048, 0x046c: 0x404c, 0x046d: 0x4051, 0x046e: 0x4055, 0x046f: 0x4059, + 0x0470: 0x405d, 0x0471: 0x4062, 0x0472: 0x4066, 0x0473: 0x406a, 0x0474: 0x406e, 0x0475: 0x2c25, + 0x0476: 0x4072, 0x0477: 0x4076, 0x0478: 0x407b, 0x0479: 0x4080, 0x047a: 0x4085, 0x047b: 0x4089, + 0x047c: 0x408e, 0x047d: 0x4092, 0x047e: 0x4096, 0x047f: 0x409a, // Block 0x12, offset 0x480 - 0x0498: 0x06de, 0x0499: 0x06e5, 0x049a: 0x06ec, 0x049b: 0x06f3, 0x049c: 0x06fa, 0x049d: 0x0701, - 0x049e: 0x0708, 0x049f: 0x070f, + 0x0480: 0x409e, 0x0481: 0x2f05, 0x0482: 0x2d71, 0x0483: 0x40a2, 0x0484: 0x40a6, 0x0485: 0x40aa, + 0x0486: 0x40ae, 0x0487: 0x40b3, 0x0488: 0x40b7, 0x0489: 0x40bb, 0x048a: 0x40bf, 0x048b: 0x3016, + 0x048c: 0x40c3, 0x048d: 0x40c7, 0x048e: 0x40cc, 0x048f: 0x40d0, 0x0490: 0x40d4, 0x0491: 0x40d9, + 0x0492: 0x40de, 0x0493: 0x40e2, 0x0494: 0x301a, 0x0495: 0x40e6, 0x0496: 0x40ea, 0x0497: 0x40ee, + 0x0498: 0x40f2, 0x0499: 0x40f6, 0x049a: 0x40fa, 0x049b: 0x40fe, 0x049c: 0x4103, 0x049d: 0x4107, + 0x049e: 0x410c, 0x049f: 0x4110, 0x04a0: 0x4115, 0x04a1: 0x3022, 0x04a2: 0x4119, 0x04a3: 0x411d, + 0x04a4: 0x4122, 0x04a5: 0x4126, 0x04a6: 0x412a, 0x04a7: 0x412f, 0x04a8: 0x4134, 0x04a9: 0x4138, + 0x04aa: 0x413c, 0x04ab: 0x4140, 0x04ac: 0x4144, 0x04ad: 0x4144, 0x04ae: 0x4148, 0x04af: 0x414c, + 0x04b0: 0x302a, 0x04b1: 0x4150, 0x04b2: 0x4154, 0x04b3: 0x4158, 0x04b4: 0x415c, 0x04b5: 0x4160, + 0x04b6: 0x4165, 0x04b7: 0x4169, 0x04b8: 0x2bed, 0x04b9: 0x416e, 0x04ba: 0x4173, 0x04bb: 0x4177, + 0x04bc: 0x417c, 0x04bd: 0x4181, 0x04be: 0x4186, 0x04bf: 0x418a, // Block 0x13, offset 0x4c0 - 0x04cb: 0x0716, - 0x04cc: 0x071d, - 0x04dc: 0x0724, 0x04dd: 0x072b, - 0x04df: 0x0732, + 0x04c0: 0x3042, 0x04c1: 0x418e, 0x04c2: 0x4193, 0x04c3: 0x4198, 0x04c4: 0x419d, 0x04c5: 0x41a2, + 0x04c6: 0x41a6, 0x04c7: 0x41a6, 0x04c8: 0x3046, 0x04c9: 0x30bd, 0x04ca: 0x41aa, 0x04cb: 0x41ae, + 0x04cc: 0x41b2, 0x04cd: 0x41b6, 0x04ce: 0x41bb, 0x04cf: 0x2b59, 0x04d0: 0x304e, 0x04d1: 0x41bf, + 0x04d2: 0x41c3, 0x04d3: 0x2f2d, 0x04d4: 0x41c8, 0x04d5: 0x41cd, 0x04d6: 0x2e89, 0x04d7: 0x41d2, + 0x04d8: 0x41d6, 0x04d9: 0x2f39, 0x04da: 0x41da, 0x04db: 0x41de, 0x04dc: 0x41e2, 0x04dd: 0x41e7, + 0x04de: 0x41e7, 0x04df: 0x41ec, 0x04e0: 0x41f0, 0x04e1: 0x41f4, 0x04e2: 0x41f9, 0x04e3: 0x41fd, + 0x04e4: 0x4201, 0x04e5: 0x4205, 0x04e6: 0x420a, 0x04e7: 0x420e, 0x04e8: 0x4212, 0x04e9: 0x4216, + 0x04ea: 0x421a, 0x04eb: 0x421e, 0x04ec: 0x4223, 0x04ed: 0x4227, 0x04ee: 0x422b, 0x04ef: 0x422f, + 0x04f0: 0x4233, 0x04f1: 0x4237, 0x04f2: 0x423b, 0x04f3: 0x4240, 0x04f4: 0x4245, 0x04f5: 0x4249, + 0x04f6: 0x424e, 0x04f7: 0x4252, 0x04f8: 0x4257, 0x04f9: 0x425b, 0x04fa: 0x2f51, 0x04fb: 0x425f, + 0x04fc: 0x4264, 0x04fd: 0x4269, 0x04fe: 0x426d, 0x04ff: 0x4272, // Block 0x14, offset 0x500 - 0x0533: 0x0739, - 0x0536: 0x0740, + 0x0500: 0x4276, 0x0501: 0x427b, 0x0502: 0x427f, 0x0503: 0x4283, 0x0504: 0x4287, 0x0505: 0x428b, + 0x0506: 0x428f, 0x0507: 0x4293, 0x0508: 0x4298, 0x0509: 0x429d, 0x050a: 0x42a2, 0x050b: 0x3f14, + 0x050c: 0x42a7, 0x050d: 0x42ab, 0x050e: 0x42af, 0x050f: 0x42b3, 0x0510: 0x42b7, 0x0511: 0x42bb, + 0x0512: 0x42bf, 0x0513: 0x42c3, 0x0514: 0x42c7, 0x0515: 0x42cb, 0x0516: 0x42cf, 0x0517: 0x42d3, + 0x0518: 0x2c31, 0x0519: 0x42d8, 0x051a: 0x42dc, 0x051b: 0x42e0, 0x051c: 0x42e4, 0x051d: 0x42e8, + 0x051e: 0x42ec, 0x051f: 0x2f5d, 0x0520: 0x42f0, 0x0521: 0x42f4, 0x0522: 0x42f8, 0x0523: 0x42fc, + 0x0524: 0x4300, 0x0525: 0x4305, 0x0526: 0x430a, 0x0527: 0x430f, 0x0528: 0x4313, 0x0529: 0x4317, + 0x052a: 0x431b, 0x052b: 0x431f, 0x052c: 0x4324, 0x052d: 0x4328, 0x052e: 0x432d, 0x052f: 0x4331, + 0x0530: 0x4335, 0x0531: 0x433a, 0x0532: 0x433f, 0x0533: 0x4343, 0x0534: 0x2b45, 0x0535: 0x4347, + 0x0536: 0x434b, 0x0537: 0x434f, 0x0538: 0x4353, 0x0539: 0x4357, 0x053a: 0x435b, 0x053b: 0x306a, + 0x053c: 0x435f, 0x053d: 0x4363, 0x053e: 0x4367, 0x053f: 0x436b, // Block 0x15, offset 0x540 - 0x0559: 0x0747, 0x055a: 0x074e, 0x055b: 0x0755, - 0x055e: 0x075c, - // Block 0x16, offset 0x580 - 0x0588: 0x0763, 0x058b: 0x076a, - 0x058c: 0x0771, - 0x059c: 0x0778, 0x059d: 0x077f, - // Block 0x17, offset 0x5c0 - 0x05d4: 0x0786, - // Block 0x18, offset 0x600 - 0x060a: 0x078d, 0x060b: 0x0794, - 0x060c: 0x079b, - // Block 0x19, offset 0x640 - 0x0648: 0x07a2, - // Block 0x1a, offset 0x680 - 0x0680: 0x07a9, - 0x0687: 0x07b0, 0x0688: 0x07b7, 0x068a: 0x07be, 0x068b: 0x07c5, - // Block 0x1b, offset 0x6c0 - 0x06ca: 0x07cf, 0x06cb: 0x07d6, - 0x06cc: 0x07dd, - // Block 0x1c, offset 0x700 - 0x071a: 0x07e4, 0x071c: 0x07eb, 0x071d: 0x07f2, - 0x071e: 0x07fc, - // Block 0x1d, offset 0x740 - 0x0743: 0x0823, - 0x074d: 0x082a, - 0x0752: 0x0831, 0x0757: 0x0838, - 0x075c: 0x083f, - 0x0769: 0x0846, - 0x0773: 0x084d, 0x0775: 0x0854, - 0x0776: 0x085b, 0x0778: 0x086c, - // Block 0x1e, offset 0x780 - 0x0781: 0x087d, - 0x0793: 0x0884, - 0x079d: 0x088b, - 0x07a2: 0x0892, - 0x07a7: 0x0899, - 0x07ac: 0x08a0, - 0x07b9: 0x08a7, - // Block 0x1f, offset 0x7c0 - 0x07e6: 0x08ae, - // Block 0x20, offset 0x800 - 0x0806: 0x08b9, 0x0808: 0x08c0, 0x080a: 0x08c7, - 0x080c: 0x08ce, 0x080e: 0x08d5, - 0x0812: 0x08dc, - 0x083b: 0x08e3, - 0x083d: 0x08ea, - // Block 0x21, offset 0x840 - 0x0840: 0x08f1, 0x0841: 0x08f8, 0x0843: 0x08ff, - // Block 0x22, offset 0x880 - 0x0880: 0x09ea, 0x0881: 0x09ee, 0x0882: 0x09f2, 0x0883: 0x09f6, 0x0884: 0x09fa, 0x0885: 0x09fe, - 0x0886: 0x0a02, 0x0887: 0x0a06, 0x0888: 0x0a0a, 0x0889: 0x0a10, 0x088a: 0x0a16, 0x088b: 0x0a1a, - 0x088c: 0x0a1e, 0x088d: 0x0a22, 0x088e: 0x0a26, 0x088f: 0x0a2a, 0x0890: 0x0a2e, 0x0891: 0x0a32, - 0x0892: 0x0a36, 0x0893: 0x0a3a, 0x0894: 0x0a3e, 0x0895: 0x0a44, 0x0896: 0x0a4a, 0x0897: 0x0a50, - 0x0898: 0x0a56, 0x0899: 0x0a5a, 0x089a: 0x0a5e, 0x089b: 0x0a62, 0x089c: 0x0a66, 0x089d: 0x0a6c, - 0x089e: 0x0a72, 0x089f: 0x0a76, 0x08a0: 0x0a7a, 0x08a1: 0x0a7e, 0x08a2: 0x0a82, 0x08a3: 0x0a86, - 0x08a4: 0x0a8a, 0x08a5: 0x0a8e, 0x08a6: 0x0a92, 0x08a7: 0x0a96, 0x08a8: 0x0a9a, 0x08a9: 0x0a9e, - 0x08aa: 0x0aa2, 0x08ab: 0x0aa6, 0x08ac: 0x0aaa, 0x08ad: 0x0aae, 0x08ae: 0x0ab2, 0x08af: 0x0ab8, - 0x08b0: 0x0abe, 0x08b1: 0x0ac2, 0x08b2: 0x0ac6, 0x08b3: 0x0aca, 0x08b4: 0x0ace, 0x08b5: 0x0ad2, - 0x08b6: 0x0ad6, 0x08b7: 0x0ada, 0x08b8: 0x0ade, 0x08b9: 0x0ae4, 0x08ba: 0x0aea, 0x08bb: 0x0aee, - 0x08bc: 0x0af2, 0x08bd: 0x0af6, 0x08be: 0x0afa, 0x08bf: 0x0afe, - // Block 0x23, offset 0x8c0 - 0x08c0: 0x0b02, 0x08c1: 0x0b06, 0x08c2: 0x0b0a, 0x08c3: 0x0b0e, 0x08c4: 0x0b12, 0x08c5: 0x0b16, - 0x08c6: 0x0b1a, 0x08c7: 0x0b1e, 0x08c8: 0x0b22, 0x08c9: 0x0b26, 0x08ca: 0x0b2a, 0x08cb: 0x0b2e, - 0x08cc: 0x0b32, 0x08cd: 0x0b38, 0x08ce: 0x0b3e, 0x08cf: 0x0b44, 0x08d0: 0x0b4a, 0x08d1: 0x0b50, - 0x08d2: 0x0b56, 0x08d3: 0x0b5c, 0x08d4: 0x0b62, 0x08d5: 0x0b66, 0x08d6: 0x0b6a, 0x08d7: 0x0b6e, - 0x08d8: 0x0b72, 0x08d9: 0x0b76, 0x08da: 0x0b7a, 0x08db: 0x0b7e, 0x08dc: 0x0b82, 0x08dd: 0x0b88, - 0x08de: 0x0b8e, 0x08df: 0x0b92, 0x08e0: 0x0b96, 0x08e1: 0x0b9a, 0x08e2: 0x0b9e, 0x08e3: 0x0ba2, - 0x08e4: 0x0ba6, 0x08e5: 0x0bac, 0x08e6: 0x0bb2, 0x08e7: 0x0bb8, 0x08e8: 0x0bbe, 0x08e9: 0x0bc4, - 0x08ea: 0x0bca, 0x08eb: 0x0bce, 0x08ec: 0x0bd2, 0x08ed: 0x0bd6, 0x08ee: 0x0bda, 0x08ef: 0x0bde, - 0x08f0: 0x0be2, 0x08f1: 0x0be6, 0x08f2: 0x0bea, 0x08f3: 0x0bee, 0x08f4: 0x0bf2, 0x08f5: 0x0bf6, - 0x08f6: 0x0bfa, 0x08f7: 0x0bfe, 0x08f8: 0x0c02, 0x08f9: 0x0c08, 0x08fa: 0x0c0e, 0x08fb: 0x0c14, - 0x08fc: 0x0c1a, 0x08fd: 0x0c1e, 0x08fe: 0x0c22, 0x08ff: 0x0c26, - // Block 0x24, offset 0x900 - 0x0900: 0x0c2a, 0x0901: 0x0c2e, 0x0902: 0x0c32, 0x0903: 0x0c36, 0x0904: 0x0c3a, 0x0905: 0x0c3e, - 0x0906: 0x0c42, 0x0907: 0x0c46, 0x0908: 0x0c4a, 0x0909: 0x0c4e, 0x090a: 0x0c52, 0x090b: 0x0c56, - 0x090c: 0x0c5a, 0x090d: 0x0c5e, 0x090e: 0x0c62, 0x090f: 0x0c66, 0x0910: 0x0c6a, 0x0911: 0x0c6e, - 0x0912: 0x0c72, 0x0913: 0x0c76, 0x0914: 0x0c7a, 0x0915: 0x0c7e, 0x0916: 0x0c82, 0x0917: 0x0c86, - 0x0918: 0x0c8a, 0x0919: 0x0c8e, 0x091b: 0x0c96, - 0x0920: 0x0c9b, 0x0921: 0x0c9f, 0x0922: 0x0ca3, 0x0923: 0x0ca7, - 0x0924: 0x0cab, 0x0925: 0x0cb1, 0x0926: 0x0cb7, 0x0927: 0x0cbd, 0x0928: 0x0cc3, 0x0929: 0x0cc9, - 0x092a: 0x0ccf, 0x092b: 0x0cd5, 0x092c: 0x0cdb, 0x092d: 0x0ce1, 0x092e: 0x0ce7, 0x092f: 0x0ced, - 0x0930: 0x0cf3, 0x0931: 0x0cf9, 0x0932: 0x0cff, 0x0933: 0x0d05, 0x0934: 0x0d0b, 0x0935: 0x0d11, - 0x0936: 0x0d17, 0x0937: 0x0d1d, 0x0938: 0x0d23, 0x0939: 0x0d27, 0x093a: 0x0d2b, 0x093b: 0x0d2f, - 0x093c: 0x0d33, 0x093d: 0x0d37, 0x093e: 0x0d3b, 0x093f: 0x0d41, - // Block 0x25, offset 0x940 - 0x0940: 0x0d47, 0x0941: 0x0d4d, 0x0942: 0x0d53, 0x0943: 0x0d59, 0x0944: 0x0d5f, 0x0945: 0x0d65, - 0x0946: 0x0d6b, 0x0947: 0x0d71, 0x0948: 0x0d77, 0x0949: 0x0d7b, 0x094a: 0x0d7f, 0x094b: 0x0d83, - 0x094c: 0x0d87, 0x094d: 0x0d8b, 0x094e: 0x0d8f, 0x094f: 0x0d93, 0x0950: 0x0d97, 0x0951: 0x0d9d, - 0x0952: 0x0da3, 0x0953: 0x0da9, 0x0954: 0x0daf, 0x0955: 0x0db5, 0x0956: 0x0dbb, 0x0957: 0x0dc1, - 0x0958: 0x0dc7, 0x0959: 0x0dcd, 0x095a: 0x0dd3, 0x095b: 0x0dd9, 0x095c: 0x0ddf, 0x095d: 0x0de5, - 0x095e: 0x0deb, 0x095f: 0x0df1, 0x0960: 0x0df7, 0x0961: 0x0dfd, 0x0962: 0x0e03, 0x0963: 0x0e09, - 0x0964: 0x0e0f, 0x0965: 0x0e13, 0x0966: 0x0e17, 0x0967: 0x0e1b, 0x0968: 0x0e1f, 0x0969: 0x0e25, - 0x096a: 0x0e2b, 0x096b: 0x0e31, 0x096c: 0x0e37, 0x096d: 0x0e3d, 0x096e: 0x0e43, 0x096f: 0x0e49, - 0x0970: 0x0e4f, 0x0971: 0x0e55, 0x0972: 0x0e5b, 0x0973: 0x0e5f, 0x0974: 0x0e63, 0x0975: 0x0e67, - 0x0976: 0x0e6b, 0x0977: 0x0e6f, 0x0978: 0x0e73, 0x0979: 0x0e77, - // Block 0x26, offset 0x980 - 0x0980: 0x0e7b, 0x0981: 0x0e80, 0x0982: 0x0e85, 0x0983: 0x0e8c, 0x0984: 0x0e93, 0x0985: 0x0e9a, - 0x0986: 0x0ea1, 0x0987: 0x0ea8, 0x0988: 0x0eaf, 0x0989: 0x0eb4, 0x098a: 0x0eb9, 0x098b: 0x0ec0, - 0x098c: 0x0ec7, 0x098d: 0x0ece, 0x098e: 0x0ed5, 0x098f: 0x0edc, 0x0990: 0x0ee3, 0x0991: 0x0ee8, - 0x0992: 0x0eed, 0x0993: 0x0ef4, 0x0994: 0x0efb, 0x0995: 0x0f02, - 0x0998: 0x0f09, 0x0999: 0x0f0e, 0x099a: 0x0f13, 0x099b: 0x0f1a, 0x099c: 0x0f21, 0x099d: 0x0f28, - 0x09a0: 0x0f2f, 0x09a1: 0x0f34, 0x09a2: 0x0f39, 0x09a3: 0x0f40, - 0x09a4: 0x0f47, 0x09a5: 0x0f4e, 0x09a6: 0x0f55, 0x09a7: 0x0f5c, 0x09a8: 0x0f63, 0x09a9: 0x0f68, - 0x09aa: 0x0f6d, 0x09ab: 0x0f74, 0x09ac: 0x0f7b, 0x09ad: 0x0f82, 0x09ae: 0x0f89, 0x09af: 0x0f90, - 0x09b0: 0x0f97, 0x09b1: 0x0f9c, 0x09b2: 0x0fa1, 0x09b3: 0x0fa8, 0x09b4: 0x0faf, 0x09b5: 0x0fb6, - 0x09b6: 0x0fbd, 0x09b7: 0x0fc4, 0x09b8: 0x0fcb, 0x09b9: 0x0fd0, 0x09ba: 0x0fd5, 0x09bb: 0x0fdc, - 0x09bc: 0x0fe3, 0x09bd: 0x0fea, 0x09be: 0x0ff1, 0x09bf: 0x0ff8, - // Block 0x27, offset 0x9c0 - 0x09c0: 0x0fff, 0x09c1: 0x1004, 0x09c2: 0x1009, 0x09c3: 0x1010, 0x09c4: 0x1017, 0x09c5: 0x101e, - 0x09c8: 0x1025, 0x09c9: 0x102a, 0x09ca: 0x102f, 0x09cb: 0x1036, - 0x09cc: 0x103d, 0x09cd: 0x1044, 0x09d0: 0x104b, 0x09d1: 0x1050, - 0x09d2: 0x1055, 0x09d3: 0x105c, 0x09d4: 0x1063, 0x09d5: 0x106a, 0x09d6: 0x1071, 0x09d7: 0x1078, - 0x09d9: 0x107f, 0x09db: 0x1084, 0x09dd: 0x108b, - 0x09df: 0x1092, 0x09e0: 0x1099, 0x09e1: 0x109e, 0x09e2: 0x10a3, 0x09e3: 0x10aa, - 0x09e4: 0x10b1, 0x09e5: 0x10b8, 0x09e6: 0x10bf, 0x09e7: 0x10c6, 0x09e8: 0x10cd, 0x09e9: 0x10d2, - 0x09ea: 0x10d7, 0x09eb: 0x10de, 0x09ec: 0x10e5, 0x09ed: 0x10ec, 0x09ee: 0x10f3, 0x09ef: 0x10fa, - 0x09f0: 0x1101, 0x09f1: 0x0525, 0x09f2: 0x1106, 0x09f3: 0x052a, 0x09f4: 0x110b, 0x09f5: 0x052f, - 0x09f6: 0x1110, 0x09f7: 0x0534, 0x09f8: 0x1115, 0x09f9: 0x054a, 0x09fa: 0x111a, 0x09fb: 0x054f, - 0x09fc: 0x111f, 0x09fd: 0x0554, - // Block 0x28, offset 0xa00 - 0x0a00: 0x1124, 0x0a01: 0x112b, 0x0a02: 0x1132, 0x0a03: 0x113b, 0x0a04: 0x1144, 0x0a05: 0x114d, - 0x0a06: 0x1156, 0x0a07: 0x115f, 0x0a08: 0x1168, 0x0a09: 0x116f, 0x0a0a: 0x1176, 0x0a0b: 0x117f, - 0x0a0c: 0x1188, 0x0a0d: 0x1191, 0x0a0e: 0x119a, 0x0a0f: 0x11a3, 0x0a10: 0x11ac, 0x0a11: 0x11b3, - 0x0a12: 0x11ba, 0x0a13: 0x11c3, 0x0a14: 0x11cc, 0x0a15: 0x11d5, 0x0a16: 0x11de, 0x0a17: 0x11e7, - 0x0a18: 0x11f0, 0x0a19: 0x11f7, 0x0a1a: 0x11fe, 0x0a1b: 0x1207, 0x0a1c: 0x1210, 0x0a1d: 0x1219, - 0x0a1e: 0x1222, 0x0a1f: 0x122b, 0x0a20: 0x1234, 0x0a21: 0x123b, 0x0a22: 0x1242, 0x0a23: 0x124b, - 0x0a24: 0x1254, 0x0a25: 0x125d, 0x0a26: 0x1266, 0x0a27: 0x126f, 0x0a28: 0x1278, 0x0a29: 0x127f, - 0x0a2a: 0x1286, 0x0a2b: 0x128f, 0x0a2c: 0x1298, 0x0a2d: 0x12a1, 0x0a2e: 0x12aa, 0x0a2f: 0x12b3, - 0x0a30: 0x12bc, 0x0a31: 0x12c1, 0x0a32: 0x12c6, 0x0a33: 0x12cd, 0x0a34: 0x12d2, - 0x0a36: 0x12d9, 0x0a37: 0x12de, 0x0a38: 0x12e5, 0x0a39: 0x12ea, 0x0a3a: 0x12ef, 0x0a3b: 0x04ee, - 0x0a3c: 0x12f4, 0x0a3e: 0x12fd, - // Block 0x29, offset 0xa40 - 0x0a41: 0x1304, 0x0a42: 0x130f, 0x0a43: 0x1316, 0x0a44: 0x131b, - 0x0a46: 0x1322, 0x0a47: 0x1327, 0x0a48: 0x132e, 0x0a49: 0x04f6, 0x0a4a: 0x1333, 0x0a4b: 0x04fb, - 0x0a4c: 0x1338, 0x0a4d: 0x133d, 0x0a4e: 0x1349, 0x0a4f: 0x1355, 0x0a50: 0x1361, 0x0a51: 0x1366, - 0x0a52: 0x136b, 0x0a53: 0x0514, 0x0a56: 0x1372, 0x0a57: 0x1377, - 0x0a58: 0x137e, 0x0a59: 0x1383, 0x0a5a: 0x1388, 0x0a5b: 0x0500, 0x0a5d: 0x138d, - 0x0a5e: 0x1399, 0x0a5f: 0x13a5, 0x0a60: 0x13b1, 0x0a61: 0x13b6, 0x0a62: 0x13bb, 0x0a63: 0x0539, - 0x0a64: 0x13c2, 0x0a65: 0x13c7, 0x0a66: 0x13cc, 0x0a67: 0x13d1, 0x0a68: 0x13d8, 0x0a69: 0x13dd, - 0x0a6a: 0x13e2, 0x0a6b: 0x050a, 0x0a6c: 0x13e7, 0x0a6d: 0x13ec, 0x0a6e: 0x04e3, 0x0a6f: 0x13f7, - 0x0a72: 0x13f9, 0x0a73: 0x1400, 0x0a74: 0x1405, - 0x0a76: 0x140c, 0x0a77: 0x1411, 0x0a78: 0x1418, 0x0a79: 0x0505, 0x0a7a: 0x141d, 0x0a7b: 0x050f, - 0x0a7c: 0x1422, 0x0a7d: 0x1427, - // Block 0x2a, offset 0xa80 - 0x0a80: 0x142e, 0x0a81: 0x1432, - // Block 0x2b, offset 0xac0 - 0x0ae6: 0x14d6, - 0x0aea: 0x091c, 0x0aeb: 0x0046, - // Block 0x2c, offset 0xb00 - 0x0b1a: 0x159f, 0x0b1b: 0x15a5, - 0x0b2e: 0x15ab, - // Block 0x2d, offset 0xb40 - 0x0b4d: 0x15b1, 0x0b4e: 0x15b7, 0x0b4f: 0x15bd, - // Block 0x2e, offset 0xb80 - 0x0b84: 0x15c3, - 0x0b89: 0x15c9, - 0x0b8c: 0x15cf, - 0x0ba4: 0x15d5, 0x0ba6: 0x15db, - // Block 0x2f, offset 0xbc0 - 0x0bc1: 0x1603, 0x0bc4: 0x1609, - 0x0bc7: 0x160f, 0x0bc9: 0x1615, - 0x0be0: 0x161b, 0x0be2: 0x161f, - 0x0bed: 0x1625, 0x0bee: 0x162b, 0x0bef: 0x162f, - 0x0bf0: 0x1633, 0x0bf1: 0x1639, 0x0bf4: 0x163f, 0x0bf5: 0x1645, - 0x0bf8: 0x164b, 0x0bf9: 0x1651, - // Block 0x30, offset 0xc00 - 0x0c00: 0x1657, 0x0c01: 0x165d, 0x0c04: 0x1663, 0x0c05: 0x1669, - 0x0c08: 0x166f, 0x0c09: 0x1675, - 0x0c2c: 0x167b, 0x0c2d: 0x1681, 0x0c2e: 0x1687, 0x0c2f: 0x168d, - // Block 0x31, offset 0xc40 - 0x0c60: 0x1693, 0x0c61: 0x1699, 0x0c62: 0x169f, 0x0c63: 0x16a5, - 0x0c6a: 0x16ab, 0x0c6b: 0x16b1, 0x0c6c: 0x16b7, 0x0c6d: 0x16bd, - // Block 0x32, offset 0xc80 - 0x0ca9: 0x16c3, - 0x0caa: 0x16c7, - // Block 0x33, offset 0xcc0 - 0x0cdc: 0x1814, - // Block 0x34, offset 0xd00 - 0x0d0c: 0x1b8a, 0x0d0e: 0x1b91, 0x0d10: 0x1b98, - 0x0d12: 0x1b9f, 0x0d14: 0x1ba6, 0x0d16: 0x1bad, - 0x0d18: 0x1bb4, 0x0d1a: 0x1bbb, 0x0d1c: 0x1bc2, - 0x0d1e: 0x1bc9, 0x0d20: 0x1bd0, 0x0d22: 0x1bd7, - 0x0d25: 0x1bde, 0x0d27: 0x1be5, 0x0d29: 0x1bec, - 0x0d30: 0x1bf3, 0x0d31: 0x1bfa, 0x0d33: 0x1c01, 0x0d34: 0x1c08, - 0x0d36: 0x1c0f, 0x0d37: 0x1c16, 0x0d39: 0x1c1d, 0x0d3a: 0x1c24, - 0x0d3c: 0x1c2b, 0x0d3d: 0x1c32, - // Block 0x35, offset 0xd40 - 0x0d54: 0x1c39, - 0x0d5e: 0x1c4a, - 0x0d6c: 0x1c58, 0x0d6e: 0x1c5f, - 0x0d70: 0x1c66, 0x0d72: 0x1c6d, 0x0d74: 0x1c74, - 0x0d76: 0x1c7b, 0x0d78: 0x1c82, 0x0d7a: 0x1c89, - 0x0d7c: 0x1c90, 0x0d7e: 0x1c97, - // Block 0x36, offset 0xd80 - 0x0d80: 0x1c9e, 0x0d82: 0x1ca5, 0x0d85: 0x1cac, - 0x0d87: 0x1cb3, 0x0d89: 0x1cba, - 0x0d90: 0x1cc1, 0x0d91: 0x1cc8, - 0x0d93: 0x1ccf, 0x0d94: 0x1cd6, 0x0d96: 0x1cdd, 0x0d97: 0x1ce4, - 0x0d99: 0x1ceb, 0x0d9a: 0x1cf2, 0x0d9c: 0x1cf9, 0x0d9d: 0x1d00, - 0x0db4: 0x1d07, - 0x0db7: 0x1d0e, 0x0db8: 0x1d15, 0x0db9: 0x1d1c, 0x0dba: 0x1d23, - 0x0dbe: 0x1d2a, - // Block 0x37, offset 0xdc0 - 0x0dc0: 0x2a81, 0x0dc1: 0x2a85, 0x0dc2: 0x1a9e, 0x0dc3: 0x2a89, 0x0dc4: 0x2a8d, 0x0dc5: 0x2a91, - 0x0dc6: 0x2a95, 0x0dc7: 0x1b76, 0x0dc8: 0x1b76, 0x0dc9: 0x2a99, 0x0dca: 0x1abe, 0x0dcb: 0x2a9d, - 0x0dcc: 0x2aa1, 0x0dcd: 0x2aa5, 0x0dce: 0x2aa9, 0x0dcf: 0x2aad, 0x0dd0: 0x2ab1, 0x0dd1: 0x2ab5, - 0x0dd2: 0x2ab9, 0x0dd3: 0x2abd, 0x0dd4: 0x2ac1, 0x0dd5: 0x2ac5, 0x0dd6: 0x2ac9, 0x0dd7: 0x2acd, - 0x0dd8: 0x2ad1, 0x0dd9: 0x2ad5, 0x0dda: 0x2ad9, 0x0ddb: 0x2add, 0x0ddc: 0x2ae1, 0x0ddd: 0x2ae5, - 0x0dde: 0x2ae9, 0x0ddf: 0x2aed, 0x0de0: 0x2af1, 0x0de1: 0x2af5, 0x0de2: 0x2af9, 0x0de3: 0x2afd, - 0x0de4: 0x2b01, 0x0de5: 0x2b05, 0x0de6: 0x2b09, 0x0de7: 0x2b0d, 0x0de8: 0x2b11, 0x0de9: 0x2b15, - 0x0dea: 0x2b19, 0x0deb: 0x2b1d, 0x0dec: 0x2b21, 0x0ded: 0x2b25, 0x0dee: 0x2b29, 0x0def: 0x2b2d, - 0x0df0: 0x2b31, 0x0df1: 0x2b35, 0x0df2: 0x2b39, 0x0df3: 0x2b3d, 0x0df4: 0x1a16, 0x0df5: 0x2b41, - 0x0df6: 0x2b45, 0x0df7: 0x2b49, 0x0df8: 0x2b4d, 0x0df9: 0x2b51, 0x0dfa: 0x2b55, 0x0dfb: 0x2b59, - 0x0dfc: 0x2b5d, 0x0dfd: 0x2b61, 0x0dfe: 0x2b65, 0x0dff: 0x2b69, - // Block 0x38, offset 0xe00 - 0x0e00: 0x1b3a, 0x0e01: 0x2b6d, 0x0e02: 0x2b71, 0x0e03: 0x2b75, 0x0e04: 0x2b79, 0x0e05: 0x2b7d, - 0x0e06: 0x2b81, 0x0e07: 0x2b85, 0x0e08: 0x2b89, 0x0e09: 0x2b8d, 0x0e0a: 0x2b91, 0x0e0b: 0x2b95, - 0x0e0c: 0x2b99, 0x0e0d: 0x2b9d, 0x0e0e: 0x2ba1, 0x0e0f: 0x2ba5, 0x0e10: 0x2ba9, 0x0e11: 0x2bad, - 0x0e12: 0x2bb1, 0x0e13: 0x2bb5, 0x0e14: 0x2bb9, 0x0e15: 0x2bbd, 0x0e16: 0x2bc1, 0x0e17: 0x2bc5, - 0x0e18: 0x2bc9, 0x0e19: 0x2bcd, 0x0e1a: 0x2bd1, 0x0e1b: 0x2bd5, 0x0e1c: 0x2ac1, 0x0e1d: 0x2bd9, - 0x0e1e: 0x2bdd, 0x0e1f: 0x2be1, 0x0e20: 0x2be5, 0x0e21: 0x2be9, 0x0e22: 0x2bed, 0x0e23: 0x2bf1, - 0x0e24: 0x2bf5, 0x0e25: 0x2bf9, 0x0e26: 0x2bfd, 0x0e27: 0x2c01, 0x0e28: 0x2c05, 0x0e29: 0x2c09, - 0x0e2a: 0x2c0d, 0x0e2b: 0x2c11, 0x0e2c: 0x2c15, 0x0e2d: 0x2c19, 0x0e2e: 0x2c1d, 0x0e2f: 0x2c21, - 0x0e30: 0x2c25, 0x0e31: 0x1aa6, 0x0e32: 0x2c29, 0x0e33: 0x2c2d, 0x0e34: 0x2c31, 0x0e35: 0x2c35, - 0x0e36: 0x2c39, 0x0e37: 0x2c3d, 0x0e38: 0x2c41, 0x0e39: 0x2c45, 0x0e3a: 0x2c49, 0x0e3b: 0x2c4d, - 0x0e3c: 0x2c51, 0x0e3d: 0x2c55, 0x0e3e: 0x2c59, 0x0e3f: 0x2c5d, - // Block 0x39, offset 0xe40 - 0x0e40: 0x2c61, 0x0e41: 0x18ba, 0x0e42: 0x2c65, 0x0e43: 0x2c69, 0x0e44: 0x2c6d, 0x0e45: 0x2c71, - 0x0e46: 0x2c75, 0x0e47: 0x2c79, 0x0e48: 0x2c7d, 0x0e49: 0x2c81, 0x0e4a: 0x186e, 0x0e4b: 0x2c85, - 0x0e4c: 0x2c89, 0x0e4d: 0x2c8d, 0x0e4e: 0x2c91, 0x0e4f: 0x2c95, 0x0e50: 0x2c99, 0x0e51: 0x2c9d, - 0x0e52: 0x2ca1, 0x0e53: 0x2ca5, 0x0e54: 0x2ca9, 0x0e55: 0x2cad, 0x0e56: 0x2cb1, 0x0e57: 0x2cb5, - 0x0e58: 0x2cb9, 0x0e59: 0x2cbd, 0x0e5a: 0x2cc1, 0x0e5b: 0x2cc5, 0x0e5c: 0x2cc9, 0x0e5d: 0x2ccd, - 0x0e5e: 0x2cd1, 0x0e5f: 0x2cd5, 0x0e60: 0x2cd9, 0x0e61: 0x2c21, 0x0e62: 0x2cdd, 0x0e63: 0x2ce1, - 0x0e64: 0x2ce5, 0x0e65: 0x2ce9, 0x0e66: 0x2ced, 0x0e67: 0x2cf1, 0x0e68: 0x2cf5, 0x0e69: 0x2cf9, - 0x0e6a: 0x2be1, 0x0e6b: 0x2cfd, 0x0e6c: 0x2d01, 0x0e6d: 0x2d05, 0x0e6e: 0x2d09, 0x0e6f: 0x2d0d, - 0x0e70: 0x2d11, 0x0e71: 0x2d15, 0x0e72: 0x2d19, 0x0e73: 0x2d1d, 0x0e74: 0x2d21, 0x0e75: 0x2d25, - 0x0e76: 0x2d29, 0x0e77: 0x2d2d, 0x0e78: 0x2d31, 0x0e79: 0x2d35, 0x0e7a: 0x2d39, 0x0e7b: 0x2d3d, - 0x0e7c: 0x2d41, 0x0e7d: 0x2d45, 0x0e7e: 0x2d49, 0x0e7f: 0x2ac1, - // Block 0x3a, offset 0xe80 - 0x0e80: 0x2d4d, 0x0e81: 0x2d51, 0x0e82: 0x2d55, 0x0e83: 0x2d59, 0x0e84: 0x1b72, 0x0e85: 0x2d5d, - 0x0e86: 0x2d61, 0x0e87: 0x2d65, 0x0e88: 0x2d69, 0x0e89: 0x2d6d, 0x0e8a: 0x2d71, 0x0e8b: 0x2d75, - 0x0e8c: 0x2d79, 0x0e8d: 0x2d7d, 0x0e8e: 0x2d81, 0x0e8f: 0x2d85, 0x0e90: 0x2d89, 0x0e91: 0x2173, - 0x0e92: 0x2d8d, 0x0e93: 0x2d91, 0x0e94: 0x2d95, 0x0e95: 0x2d99, 0x0e96: 0x2d9d, 0x0e97: 0x2da1, - 0x0e98: 0x2da5, 0x0e99: 0x2da9, 0x0e9a: 0x2dad, 0x0e9b: 0x2be9, 0x0e9c: 0x2db1, 0x0e9d: 0x2db5, - 0x0e9e: 0x2db9, 0x0e9f: 0x2dbd, 0x0ea0: 0x2dc1, 0x0ea1: 0x2dc5, 0x0ea2: 0x2dc9, 0x0ea3: 0x2dcd, - 0x0ea4: 0x2dd1, 0x0ea5: 0x2dd5, 0x0ea6: 0x2dd9, 0x0ea7: 0x2ddd, 0x0ea8: 0x2de1, 0x0ea9: 0x1aba, - 0x0eaa: 0x2de5, 0x0eab: 0x2de9, 0x0eac: 0x2ded, 0x0ead: 0x2df1, 0x0eae: 0x2df5, 0x0eaf: 0x2df9, - 0x0eb0: 0x2dfd, 0x0eb1: 0x2e01, 0x0eb2: 0x2e05, 0x0eb3: 0x2e09, 0x0eb4: 0x2e0d, 0x0eb5: 0x2e11, - 0x0eb6: 0x2e15, 0x0eb7: 0x19f6, 0x0eb8: 0x2e19, 0x0eb9: 0x2e1d, 0x0eba: 0x2e21, 0x0ebb: 0x2e25, - 0x0ebc: 0x2e29, 0x0ebd: 0x2e2d, 0x0ebe: 0x2e31, 0x0ebf: 0x2e35, - // Block 0x3b, offset 0xec0 - 0x0ec0: 0x2e39, 0x0ec1: 0x2e3d, 0x0ec2: 0x2e41, 0x0ec3: 0x2e45, 0x0ec4: 0x2e49, 0x0ec5: 0x2e4d, - 0x0ec6: 0x2e51, 0x0ec7: 0x2e55, 0x0ec8: 0x1a62, 0x0ec9: 0x2e59, 0x0eca: 0x1a6e, 0x0ecb: 0x2e5d, - 0x0ecc: 0x2e61, 0x0ecd: 0x2e65, 0x0ed0: 0x2e69, - 0x0ed2: 0x2e6d, 0x0ed5: 0x2e71, 0x0ed6: 0x2e75, 0x0ed7: 0x2e79, - 0x0ed8: 0x2e7d, 0x0ed9: 0x2e81, 0x0eda: 0x2e85, 0x0edb: 0x2e89, 0x0edc: 0x2e8d, 0x0edd: 0x2e91, - 0x0ede: 0x1a12, 0x0ee0: 0x2e95, 0x0ee2: 0x2e99, - 0x0ee5: 0x2e9d, 0x0ee6: 0x2ea1, - 0x0eea: 0x2ea5, 0x0eeb: 0x2ea9, 0x0eec: 0x2ead, 0x0eed: 0x2eb1, - 0x0ef0: 0x2eb5, 0x0ef1: 0x2eb9, 0x0ef2: 0x2ebd, 0x0ef3: 0x2ec1, 0x0ef4: 0x2ec5, 0x0ef5: 0x2ec9, - 0x0ef6: 0x2ecd, 0x0ef7: 0x2ed1, 0x0ef8: 0x2ed5, 0x0ef9: 0x2ed9, 0x0efa: 0x2edd, 0x0efb: 0x2ee1, - 0x0efc: 0x18d6, 0x0efd: 0x2ee5, 0x0efe: 0x2ee9, 0x0eff: 0x2eed, - // Block 0x3c, offset 0xf00 - 0x0f00: 0x2ef1, 0x0f01: 0x2ef5, 0x0f02: 0x2ef9, 0x0f03: 0x2efd, 0x0f04: 0x2f01, 0x0f05: 0x2f05, - 0x0f06: 0x2f09, 0x0f07: 0x2f0d, 0x0f08: 0x2f11, 0x0f09: 0x2f15, 0x0f0a: 0x2f19, 0x0f0b: 0x2f1d, - 0x0f0c: 0x2187, 0x0f0d: 0x2f21, 0x0f0e: 0x2f25, 0x0f0f: 0x2f29, 0x0f10: 0x2f2d, 0x0f11: 0x2197, - 0x0f12: 0x2f31, 0x0f13: 0x2f35, 0x0f14: 0x2f39, 0x0f15: 0x2f3d, 0x0f16: 0x2f41, 0x0f17: 0x2cb1, - 0x0f18: 0x2f45, 0x0f19: 0x2f49, 0x0f1a: 0x2f4d, 0x0f1b: 0x2f51, 0x0f1c: 0x2f55, 0x0f1d: 0x2f59, - 0x0f1e: 0x2f59, 0x0f1f: 0x2f5d, 0x0f20: 0x2f61, 0x0f21: 0x2f65, 0x0f22: 0x2f69, 0x0f23: 0x2f6d, - 0x0f24: 0x2f71, 0x0f25: 0x2f75, 0x0f26: 0x2f79, 0x0f27: 0x2e9d, 0x0f28: 0x2f7d, 0x0f29: 0x2f81, - 0x0f2a: 0x2f85, 0x0f2b: 0x2f89, 0x0f2c: 0x2f8d, 0x0f2d: 0x2f92, - 0x0f30: 0x2f96, 0x0f31: 0x2f9a, 0x0f32: 0x2f9e, 0x0f33: 0x2fa2, 0x0f34: 0x2fa6, 0x0f35: 0x2faa, - 0x0f36: 0x2fae, 0x0f37: 0x2fb2, 0x0f38: 0x2ecd, 0x0f39: 0x2fb6, 0x0f3a: 0x2fba, 0x0f3b: 0x2fbe, - 0x0f3c: 0x2e69, 0x0f3d: 0x2fc2, 0x0f3e: 0x2fc6, 0x0f3f: 0x2fca, - // Block 0x3d, offset 0xf40 - 0x0f40: 0x2fce, 0x0f41: 0x2fd2, 0x0f42: 0x2fd6, 0x0f43: 0x2fda, 0x0f44: 0x2fde, 0x0f45: 0x2fe2, - 0x0f46: 0x2fe6, 0x0f47: 0x2fea, 0x0f48: 0x2fee, 0x0f49: 0x2eed, 0x0f4a: 0x2ff2, 0x0f4b: 0x2ef1, - 0x0f4c: 0x2ff6, 0x0f4d: 0x2ffa, 0x0f4e: 0x2ffe, 0x0f4f: 0x3002, 0x0f50: 0x3006, 0x0f51: 0x2e6d, - 0x0f52: 0x2b15, 0x0f53: 0x300a, 0x0f54: 0x300e, 0x0f55: 0x195a, 0x0f56: 0x2c25, 0x0f57: 0x2d71, - 0x0f58: 0x3012, 0x0f59: 0x3016, 0x0f5a: 0x2f0d, 0x0f5b: 0x301a, 0x0f5c: 0x2f11, 0x0f5d: 0x301e, - 0x0f5e: 0x3022, 0x0f5f: 0x3026, 0x0f60: 0x2e75, 0x0f61: 0x302a, 0x0f62: 0x302e, 0x0f63: 0x3032, - 0x0f64: 0x3036, 0x0f65: 0x303a, 0x0f66: 0x2e79, 0x0f67: 0x303e, 0x0f68: 0x3042, 0x0f69: 0x3046, - 0x0f6a: 0x304a, 0x0f6b: 0x304e, 0x0f6c: 0x3052, 0x0f6d: 0x2f41, 0x0f6e: 0x3056, 0x0f6f: 0x305a, - 0x0f70: 0x2cb1, 0x0f71: 0x305e, 0x0f72: 0x2f51, 0x0f73: 0x3062, 0x0f74: 0x3066, 0x0f75: 0x306a, - 0x0f76: 0x306e, 0x0f77: 0x3072, 0x0f78: 0x2f65, 0x0f79: 0x3076, 0x0f7a: 0x2e99, 0x0f7b: 0x307a, - 0x0f7c: 0x2f69, 0x0f7d: 0x2bd9, 0x0f7e: 0x307e, 0x0f7f: 0x2f6d, - // Block 0x3e, offset 0xf80 - 0x0f80: 0x3082, 0x0f81: 0x2f75, 0x0f82: 0x3086, 0x0f83: 0x308a, 0x0f84: 0x308e, 0x0f85: 0x3092, - 0x0f86: 0x3096, 0x0f87: 0x2f7d, 0x0f88: 0x2e8d, 0x0f89: 0x309a, 0x0f8a: 0x2f81, 0x0f8b: 0x309e, - 0x0f8c: 0x2f85, 0x0f8d: 0x30a2, 0x0f8e: 0x1b76, 0x0f8f: 0x30a6, 0x0f90: 0x30ab, 0x0f91: 0x30b0, - 0x0f92: 0x30b5, 0x0f93: 0x30b9, 0x0f94: 0x30bd, 0x0f95: 0x30c1, 0x0f96: 0x30c6, 0x0f97: 0x30cb, - 0x0f98: 0x30d0, 0x0f99: 0x30d4, - // Block 0x3f, offset 0xfc0 - 0x0fdd: 0x3105, - 0x0fdf: 0x310a, - 0x0fea: 0x3124, 0x0feb: 0x3129, 0x0fec: 0x312e, 0x0fed: 0x3135, 0x0fee: 0x313c, 0x0fef: 0x3141, - 0x0ff0: 0x3146, 0x0ff1: 0x314b, 0x0ff2: 0x3150, 0x0ff3: 0x3155, 0x0ff4: 0x315a, 0x0ff5: 0x315f, - 0x0ff6: 0x3164, 0x0ff8: 0x3169, 0x0ff9: 0x316e, 0x0ffa: 0x3173, 0x0ffb: 0x3178, - 0x0ffc: 0x317d, 0x0ffe: 0x3182, - // Block 0x40, offset 0x1000 - 0x1000: 0x3187, 0x1001: 0x318c, 0x1003: 0x3191, 0x1004: 0x3196, - 0x1006: 0x319b, 0x1007: 0x31a0, 0x1008: 0x31a5, 0x1009: 0x31aa, 0x100a: 0x31af, 0x100b: 0x31b4, - 0x100c: 0x31b9, 0x100d: 0x31be, 0x100e: 0x31c3, - // Block 0x41, offset 0x1040 - 0x105a: 0x3a73, 0x105c: 0x3a7c, - 0x106b: 0x3a85, - // Block 0x42, offset 0x1080 - 0x109e: 0x3a8e, 0x109f: 0x3a97, 0x10a0: 0x3aa0, 0x10a1: 0x3aad, 0x10a2: 0x3aba, 0x10a3: 0x3ac7, - 0x10a4: 0x3ad4, - // Block 0x43, offset 0x10c0 - 0x10fb: 0x3ae1, - 0x10fc: 0x3aea, 0x10fd: 0x3af3, 0x10fe: 0x3b00, 0x10ff: 0x3b0d, - // Block 0x44, offset 0x1100 - 0x1100: 0x3b1a, - // Block 0x45, offset 0x1140 - 0x1140: 0x3d23, 0x1141: 0x3d27, 0x1142: 0x3d2b, 0x1143: 0x3d2f, 0x1144: 0x3d34, 0x1145: 0x2eb5, - 0x1146: 0x3d38, 0x1147: 0x3d3c, 0x1148: 0x3d40, 0x1149: 0x3d44, 0x114a: 0x2eb9, 0x114b: 0x3d48, - 0x114c: 0x3d4c, 0x114d: 0x3d50, 0x114e: 0x2ebd, 0x114f: 0x3d55, 0x1150: 0x3d59, 0x1151: 0x3d5d, - 0x1152: 0x3d61, 0x1153: 0x3d66, 0x1154: 0x3d6a, 0x1155: 0x3c71, 0x1156: 0x3d6e, 0x1157: 0x3d73, - 0x1158: 0x3d77, 0x1159: 0x3d7b, 0x115a: 0x3d7f, 0x115b: 0x2f9a, 0x115c: 0x3d83, 0x115d: 0x1866, - 0x115e: 0x3d88, 0x115f: 0x3d8c, 0x1160: 0x3d90, 0x1161: 0x3d94, 0x1162: 0x3cb9, 0x1163: 0x3d98, - 0x1164: 0x3d9c, 0x1165: 0x2fae, 0x1166: 0x2ec1, 0x1167: 0x2ec5, 0x1168: 0x2fb2, 0x1169: 0x3da0, - 0x116a: 0x3da4, 0x116b: 0x2bf1, 0x116c: 0x3da8, 0x116d: 0x2ec9, 0x116e: 0x3dac, 0x116f: 0x3db0, - 0x1170: 0x3db4, 0x1171: 0x3db8, 0x1172: 0x3db8, 0x1173: 0x3db8, 0x1174: 0x3dbc, 0x1175: 0x3dc1, - 0x1176: 0x3dc5, 0x1177: 0x3dc9, 0x1178: 0x3dcd, 0x1179: 0x3dd2, 0x117a: 0x3dd6, 0x117b: 0x3dda, - 0x117c: 0x3dde, 0x117d: 0x3de2, 0x117e: 0x3de6, 0x117f: 0x3dea, - // Block 0x46, offset 0x1180 - 0x1180: 0x3dee, 0x1181: 0x3df2, 0x1182: 0x3df6, 0x1183: 0x3dfa, 0x1184: 0x3dfe, 0x1185: 0x3e02, - 0x1186: 0x3e02, 0x1187: 0x2fba, 0x1188: 0x3e06, 0x1189: 0x3e0a, 0x118a: 0x3e0e, 0x118b: 0x3e12, - 0x118c: 0x2ed1, 0x118d: 0x3e16, 0x118e: 0x3e1a, 0x118f: 0x3e1e, 0x1190: 0x2e39, 0x1191: 0x3e22, - 0x1192: 0x3e26, 0x1193: 0x3e2a, 0x1194: 0x3e2e, 0x1195: 0x3e32, 0x1196: 0x3e36, 0x1197: 0x3e3a, - 0x1198: 0x3e3e, 0x1199: 0x3e42, 0x119a: 0x3e47, 0x119b: 0x3e4b, 0x119c: 0x3e4f, 0x119d: 0x3c55, - 0x119e: 0x3e53, 0x119f: 0x3e57, 0x11a0: 0x3e5b, 0x11a1: 0x3e60, 0x11a2: 0x3e65, 0x11a3: 0x3e69, - 0x11a4: 0x3e6d, 0x11a5: 0x3e71, 0x11a6: 0x3e75, 0x11a7: 0x3e79, 0x11a8: 0x3e7d, 0x11a9: 0x3e81, - 0x11aa: 0x3e85, 0x11ab: 0x3e85, 0x11ac: 0x3e89, 0x11ad: 0x3e8e, 0x11ae: 0x3e92, 0x11af: 0x2be1, - 0x11b0: 0x3e96, 0x11b1: 0x3e9a, 0x11b2: 0x3e9f, 0x11b3: 0x3ea3, 0x11b4: 0x3ea7, 0x11b5: 0x18ce, - 0x11b6: 0x3eab, 0x11b7: 0x3eaf, 0x11b8: 0x18d6, 0x11b9: 0x3eb3, 0x11ba: 0x3eb7, 0x11bb: 0x3ebb, - 0x11bc: 0x3ec0, 0x11bd: 0x3ec4, 0x11be: 0x3ec9, 0x11bf: 0x3ecd, - // Block 0x47, offset 0x11c0 - 0x11c0: 0x3ed1, 0x11c1: 0x3ed5, 0x11c2: 0x3ed9, 0x11c3: 0x3edd, 0x11c4: 0x3ee1, 0x11c5: 0x3ee5, - 0x11c6: 0x3ee9, 0x11c7: 0x3eed, 0x11c8: 0x3ef1, 0x11c9: 0x3ef5, 0x11ca: 0x3efa, 0x11cb: 0x3efe, - 0x11cc: 0x3f02, 0x11cd: 0x3f06, 0x11ce: 0x2b11, 0x11cf: 0x3f0a, 0x11d0: 0x18fe, 0x11d1: 0x3f0f, - 0x11d2: 0x3f0f, 0x11d3: 0x3f14, 0x11d4: 0x3f18, 0x11d5: 0x3f18, 0x11d6: 0x3f1c, 0x11d7: 0x3f20, - 0x11d8: 0x3f25, 0x11d9: 0x3f2a, 0x11da: 0x3f2e, 0x11db: 0x3f32, 0x11dc: 0x3f36, 0x11dd: 0x3f3a, - 0x11de: 0x3f3e, 0x11df: 0x3f42, 0x11e0: 0x3f46, 0x11e1: 0x3f4a, 0x11e2: 0x3f4e, 0x11e3: 0x2ee5, - 0x11e4: 0x3f52, 0x11e5: 0x3f57, 0x11e6: 0x3f5b, 0x11e7: 0x3f5f, 0x11e8: 0x2fea, 0x11e9: 0x3f5f, - 0x11ea: 0x3f63, 0x11eb: 0x2eed, 0x11ec: 0x3f67, 0x11ed: 0x3f6b, 0x11ee: 0x3f6f, 0x11ef: 0x3f73, - 0x11f0: 0x2ef1, 0x11f1: 0x2aa5, 0x11f2: 0x3f77, 0x11f3: 0x3f7b, 0x11f4: 0x3f7f, 0x11f5: 0x3f83, - 0x11f6: 0x3f87, 0x11f7: 0x3f8b, 0x11f8: 0x3f8f, 0x11f9: 0x3f94, 0x11fa: 0x3f98, 0x11fb: 0x3f9c, - 0x11fc: 0x3fa0, 0x11fd: 0x3fa4, 0x11fe: 0x3fa8, 0x11ff: 0x3fad, - // Block 0x48, offset 0x1200 - 0x1200: 0x3fb1, 0x1201: 0x3fb5, 0x1202: 0x3fb9, 0x1203: 0x3fbd, 0x1204: 0x3fc1, 0x1205: 0x3fc5, - 0x1206: 0x3fc9, 0x1207: 0x3fcd, 0x1208: 0x2ef5, 0x1209: 0x3fd1, 0x120a: 0x3fd5, 0x120b: 0x3fda, - 0x120c: 0x3fde, 0x120d: 0x3fe2, 0x120e: 0x3fe6, 0x120f: 0x2efd, 0x1210: 0x3fea, 0x1211: 0x3fee, - 0x1212: 0x3ff2, 0x1213: 0x3ff6, 0x1214: 0x3ffa, 0x1215: 0x3ffe, 0x1216: 0x4002, 0x1217: 0x4006, - 0x1218: 0x2b15, 0x1219: 0x300a, 0x121a: 0x400a, 0x121b: 0x400e, 0x121c: 0x4012, 0x121d: 0x4016, - 0x121e: 0x401b, 0x121f: 0x401f, 0x1220: 0x4023, 0x1221: 0x4027, 0x1222: 0x2f01, 0x1223: 0x402b, - 0x1224: 0x4030, 0x1225: 0x4034, 0x1226: 0x4038, 0x1227: 0x30b5, 0x1228: 0x403c, 0x1229: 0x4040, - 0x122a: 0x4044, 0x122b: 0x4048, 0x122c: 0x404c, 0x122d: 0x4051, 0x122e: 0x4055, 0x122f: 0x4059, - 0x1230: 0x405d, 0x1231: 0x4062, 0x1232: 0x4066, 0x1233: 0x406a, 0x1234: 0x406e, 0x1235: 0x2c25, - 0x1236: 0x4072, 0x1237: 0x4076, 0x1238: 0x407b, 0x1239: 0x4080, 0x123a: 0x4085, 0x123b: 0x4089, - 0x123c: 0x408e, 0x123d: 0x4092, 0x123e: 0x4096, 0x123f: 0x409a, - // Block 0x49, offset 0x1240 - 0x1240: 0x409e, 0x1241: 0x2f05, 0x1242: 0x2d71, 0x1243: 0x40a2, 0x1244: 0x40a6, 0x1245: 0x40aa, - 0x1246: 0x40ae, 0x1247: 0x40b3, 0x1248: 0x40b7, 0x1249: 0x40bb, 0x124a: 0x40bf, 0x124b: 0x3016, - 0x124c: 0x40c3, 0x124d: 0x40c7, 0x124e: 0x40cc, 0x124f: 0x40d0, 0x1250: 0x40d4, 0x1251: 0x40d9, - 0x1252: 0x40de, 0x1253: 0x40e2, 0x1254: 0x301a, 0x1255: 0x40e6, 0x1256: 0x40ea, 0x1257: 0x40ee, - 0x1258: 0x40f2, 0x1259: 0x40f6, 0x125a: 0x40fa, 0x125b: 0x40fe, 0x125c: 0x4103, 0x125d: 0x4107, - 0x125e: 0x410c, 0x125f: 0x4110, 0x1260: 0x4115, 0x1261: 0x3022, 0x1262: 0x4119, 0x1263: 0x411d, - 0x1264: 0x4122, 0x1265: 0x4126, 0x1266: 0x412a, 0x1267: 0x412f, 0x1268: 0x4134, 0x1269: 0x4138, - 0x126a: 0x413c, 0x126b: 0x4140, 0x126c: 0x4144, 0x126d: 0x4144, 0x126e: 0x4148, 0x126f: 0x414c, - 0x1270: 0x302a, 0x1271: 0x4150, 0x1272: 0x4154, 0x1273: 0x4158, 0x1274: 0x415c, 0x1275: 0x4160, - 0x1276: 0x4165, 0x1277: 0x4169, 0x1278: 0x2bed, 0x1279: 0x416e, 0x127a: 0x4173, 0x127b: 0x4177, - 0x127c: 0x417c, 0x127d: 0x4181, 0x127e: 0x4186, 0x127f: 0x418a, - // Block 0x4a, offset 0x1280 - 0x1280: 0x3042, 0x1281: 0x418e, 0x1282: 0x4193, 0x1283: 0x4198, 0x1284: 0x419d, 0x1285: 0x41a2, - 0x1286: 0x41a6, 0x1287: 0x41a6, 0x1288: 0x3046, 0x1289: 0x30bd, 0x128a: 0x41aa, 0x128b: 0x41ae, - 0x128c: 0x41b2, 0x128d: 0x41b6, 0x128e: 0x41bb, 0x128f: 0x2b59, 0x1290: 0x304e, 0x1291: 0x41bf, - 0x1292: 0x41c3, 0x1293: 0x2f2d, 0x1294: 0x41c8, 0x1295: 0x41cd, 0x1296: 0x2e89, 0x1297: 0x41d2, - 0x1298: 0x41d6, 0x1299: 0x2f39, 0x129a: 0x41da, 0x129b: 0x41de, 0x129c: 0x41e2, 0x129d: 0x41e7, - 0x129e: 0x41e7, 0x129f: 0x41ec, 0x12a0: 0x41f0, 0x12a1: 0x41f4, 0x12a2: 0x41f9, 0x12a3: 0x41fd, - 0x12a4: 0x4201, 0x12a5: 0x4205, 0x12a6: 0x420a, 0x12a7: 0x420e, 0x12a8: 0x4212, 0x12a9: 0x4216, - 0x12aa: 0x421a, 0x12ab: 0x421e, 0x12ac: 0x4223, 0x12ad: 0x4227, 0x12ae: 0x422b, 0x12af: 0x422f, - 0x12b0: 0x4233, 0x12b1: 0x4237, 0x12b2: 0x423b, 0x12b3: 0x4240, 0x12b4: 0x4245, 0x12b5: 0x4249, - 0x12b6: 0x424e, 0x12b7: 0x4252, 0x12b8: 0x4257, 0x12b9: 0x425b, 0x12ba: 0x2f51, 0x12bb: 0x425f, - 0x12bc: 0x4264, 0x12bd: 0x4269, 0x12be: 0x426d, 0x12bf: 0x4272, - // Block 0x4b, offset 0x12c0 - 0x12c0: 0x4276, 0x12c1: 0x427b, 0x12c2: 0x427f, 0x12c3: 0x4283, 0x12c4: 0x4287, 0x12c5: 0x428b, - 0x12c6: 0x428f, 0x12c7: 0x4293, 0x12c8: 0x4298, 0x12c9: 0x429d, 0x12ca: 0x42a2, 0x12cb: 0x3f14, - 0x12cc: 0x42a7, 0x12cd: 0x42ab, 0x12ce: 0x42af, 0x12cf: 0x42b3, 0x12d0: 0x42b7, 0x12d1: 0x42bb, - 0x12d2: 0x42bf, 0x12d3: 0x42c3, 0x12d4: 0x42c7, 0x12d5: 0x42cb, 0x12d6: 0x42cf, 0x12d7: 0x42d3, - 0x12d8: 0x2c31, 0x12d9: 0x42d8, 0x12da: 0x42dc, 0x12db: 0x42e0, 0x12dc: 0x42e4, 0x12dd: 0x42e8, - 0x12de: 0x42ec, 0x12df: 0x2f5d, 0x12e0: 0x42f0, 0x12e1: 0x42f4, 0x12e2: 0x42f8, 0x12e3: 0x42fc, - 0x12e4: 0x4300, 0x12e5: 0x4305, 0x12e6: 0x430a, 0x12e7: 0x430f, 0x12e8: 0x4313, 0x12e9: 0x4317, - 0x12ea: 0x431b, 0x12eb: 0x431f, 0x12ec: 0x4324, 0x12ed: 0x4328, 0x12ee: 0x432d, 0x12ef: 0x4331, - 0x12f0: 0x4335, 0x12f1: 0x433a, 0x12f2: 0x433f, 0x12f3: 0x4343, 0x12f4: 0x2b45, 0x12f5: 0x4347, - 0x12f6: 0x434b, 0x12f7: 0x434f, 0x12f8: 0x4353, 0x12f9: 0x4357, 0x12fa: 0x435b, 0x12fb: 0x306a, - 0x12fc: 0x435f, 0x12fd: 0x4363, 0x12fe: 0x4367, 0x12ff: 0x436b, - // Block 0x4c, offset 0x1300 - 0x1300: 0x436f, 0x1301: 0x4373, 0x1302: 0x4377, 0x1303: 0x437b, 0x1304: 0x1a66, 0x1305: 0x437f, - 0x1306: 0x4384, 0x1307: 0x4388, 0x1308: 0x438c, 0x1309: 0x4390, 0x130a: 0x4394, 0x130b: 0x4398, - 0x130c: 0x439d, 0x130d: 0x43a2, 0x130e: 0x43a6, 0x130f: 0x43aa, 0x1310: 0x307e, 0x1311: 0x3082, - 0x1312: 0x1a82, 0x1313: 0x43ae, 0x1314: 0x43b3, 0x1315: 0x43b7, 0x1316: 0x43bb, 0x1317: 0x43bf, - 0x1318: 0x43c3, 0x1319: 0x43c8, 0x131a: 0x43cd, 0x131b: 0x43d1, 0x131c: 0x43d5, 0x131d: 0x43d9, - 0x131e: 0x43de, 0x131f: 0x3086, 0x1320: 0x43e2, 0x1321: 0x43e7, 0x1322: 0x43ec, 0x1323: 0x43f0, - 0x1324: 0x43f4, 0x1325: 0x43f8, 0x1326: 0x43fd, 0x1327: 0x4401, 0x1328: 0x4405, 0x1329: 0x4409, - 0x132a: 0x440d, 0x132b: 0x4411, 0x132c: 0x4415, 0x132d: 0x4419, 0x132e: 0x441e, 0x132f: 0x4422, - 0x1330: 0x4426, 0x1331: 0x442a, 0x1332: 0x442f, 0x1333: 0x4433, 0x1334: 0x4437, 0x1335: 0x443b, - 0x1336: 0x443f, 0x1337: 0x4444, 0x1338: 0x4449, 0x1339: 0x444d, 0x133a: 0x4451, 0x133b: 0x4455, - 0x133c: 0x445a, 0x133d: 0x445e, 0x133e: 0x309e, 0x133f: 0x309e, - // Block 0x4d, offset 0x1340 - 0x1340: 0x4463, 0x1341: 0x4467, 0x1342: 0x446c, 0x1343: 0x4470, 0x1344: 0x4474, 0x1345: 0x4478, - 0x1346: 0x447c, 0x1347: 0x4480, 0x1348: 0x4484, 0x1349: 0x4488, 0x134a: 0x30a2, 0x134b: 0x448d, - 0x134c: 0x4491, 0x134d: 0x4495, 0x134e: 0x4499, 0x134f: 0x449d, 0x1350: 0x44a1, 0x1351: 0x44a6, - 0x1352: 0x44aa, 0x1353: 0x44af, 0x1354: 0x44b4, 0x1355: 0x1b42, 0x1356: 0x44b9, 0x1357: 0x1b52, - 0x1358: 0x44bd, 0x1359: 0x44c1, 0x135a: 0x44c5, 0x135b: 0x44c9, 0x135c: 0x1b66, 0x135d: 0x44cd, + 0x0540: 0x436f, 0x0541: 0x4373, 0x0542: 0x4377, 0x0543: 0x437b, 0x0544: 0x1a66, 0x0545: 0x437f, + 0x0546: 0x4384, 0x0547: 0x4388, 0x0548: 0x438c, 0x0549: 0x4390, 0x054a: 0x4394, 0x054b: 0x4398, + 0x054c: 0x439d, 0x054d: 0x43a2, 0x054e: 0x43a6, 0x054f: 0x43aa, 0x0550: 0x307e, 0x0551: 0x3082, + 0x0552: 0x1a82, 0x0553: 0x43ae, 0x0554: 0x43b3, 0x0555: 0x43b7, 0x0556: 0x43bb, 0x0557: 0x43bf, + 0x0558: 0x43c3, 0x0559: 0x43c8, 0x055a: 0x43cd, 0x055b: 0x43d1, 0x055c: 0x43d5, 0x055d: 0x43d9, + 0x055e: 0x43de, 0x055f: 0x3086, 0x0560: 0x43e2, 0x0561: 0x43e7, 0x0562: 0x43ec, 0x0563: 0x43f0, + 0x0564: 0x43f4, 0x0565: 0x43f8, 0x0566: 0x43fd, 0x0567: 0x4401, 0x0568: 0x4405, 0x0569: 0x4409, + 0x056a: 0x440d, 0x056b: 0x4411, 0x056c: 0x4415, 0x056d: 0x4419, 0x056e: 0x441e, 0x056f: 0x4422, + 0x0570: 0x4426, 0x0571: 0x442a, 0x0572: 0x442f, 0x0573: 0x4433, 0x0574: 0x4437, 0x0575: 0x443b, + 0x0576: 0x443f, 0x0577: 0x4444, 0x0578: 0x4449, 0x0579: 0x444d, 0x057a: 0x4451, 0x057b: 0x4455, + 0x057c: 0x445a, 0x057d: 0x445e, 0x057e: 0x309e, 0x057f: 0x309e, +} + +// nfcDecompSparseOffset: 56 entries, 112 bytes +var nfcDecompSparseOffset = []uint16{0x0, 0xa, 0x10, 0x15, 0x18, 0x22, 0x27, 0x2e, 0x31, 0x38, 0x3e, 0x46, 0x48, 0x4c, 0x50, 0x52, 0x56, 0x59, 0x5c, 0x60, 0x62, 0x64, 0x66, 0x6a, 0x6c, 0x70, 0x7a, 0x82, 0x84, 0x8d, 0x90, 0x9e, 0xa0, 0xa4, 0xa7, 0xa9, 0xaf, 0xbb, 0xc0, 0xc3, 0xc5, 0xc7, 0xd4, 0xe2, 0xed, 0xf4, 0xff, 0x10b, 0x11c, 0x12d, 0x135, 0x139, 0x13d, 0x141, 0x145, 0x147} + +// nfcDecompSparseValues: 341 entries, 1364 bytes +var nfcDecompSparseValues = [341]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0004, lo: 0x09}, + {value: 0x0032, lo: 0x80, hi: 0x85}, + {value: 0x004a, lo: 0x87, hi: 0x8f}, + {value: 0x006e, lo: 0x91, hi: 0x96}, + {value: 0x0086, lo: 0x99, hi: 0x9d}, + {value: 0x009a, lo: 0xa0, hi: 0xa5}, + {value: 0x00b2, lo: 0xa7, hi: 0xaf}, + {value: 0x00d6, lo: 0xb1, hi: 0xb6}, + {value: 0x00ee, lo: 0xb9, hi: 0xbd}, + {value: 0x0102, lo: 0xbf, hi: 0xbf}, + // Block 0x1, offset 0x2 + {value: 0x0004, lo: 0x05}, + {value: 0x0106, lo: 0x80, hi: 0x8f}, + {value: 0x0146, lo: 0x92, hi: 0xa5}, + {value: 0x0196, lo: 0xa8, hi: 0xb0}, + {value: 0x01c0, lo: 0xb4, hi: 0xb7}, + {value: 0x01d0, lo: 0xb9, hi: 0xbe}, + // Block 0x2, offset 0x3 + {value: 0x0004, lo: 0x04}, + {value: 0x01f0, lo: 0x83, hi: 0x88}, + {value: 0x020c, lo: 0x8c, hi: 0x91}, + {value: 0x0224, lo: 0x94, hi: 0xa5}, + {value: 0x026c, lo: 0xa8, hi: 0xbe}, + // Block 0x3, offset 0x4 + {value: 0x0004, lo: 0x02}, + {value: 0x02ca, lo: 0xa0, hi: 0xa1}, + {value: 0x02d2, lo: 0xaf, hi: 0xb0}, + // Block 0x4, offset 0x5 + {value: 0x0004, lo: 0x09}, + {value: 0x03d8, lo: 0x80, hi: 0x9b}, + {value: 0x0448, lo: 0x9e, hi: 0x9f}, + {value: 0x0450, lo: 0xa6, hi: 0xaa}, + {value: 0x0466, lo: 0xab, hi: 0xab}, + {value: 0x046c, lo: 0xac, hi: 0xac}, + {value: 0x0472, lo: 0xad, hi: 0xad}, + {value: 0x0478, lo: 0xae, hi: 0xb0}, + {value: 0x0486, lo: 0xb1, hi: 0xb1}, + {value: 0x048c, lo: 0xb2, hi: 0xb3}, + // Block 0x5, offset 0x6 + {value: 0x0003, lo: 0x04}, + {value: 0x04cc, lo: 0x80, hi: 0x81}, + {value: 0x04d2, lo: 0x83, hi: 0x84}, + {value: 0x04da, lo: 0xb4, hi: 0xb4}, + {value: 0x04e1, lo: 0xbe, hi: 0xbe}, + // Block 0x6, offset 0x7 + {value: 0x0005, lo: 0x06}, + {value: 0x04e3, lo: 0x85, hi: 0x85}, + {value: 0x04ee, lo: 0x86, hi: 0x87}, + {value: 0x04f6, lo: 0x88, hi: 0x8a}, + {value: 0x0505, lo: 0x8c, hi: 0x8c}, + {value: 0x050a, lo: 0x8e, hi: 0x90}, + {value: 0x051b, lo: 0xaa, hi: 0xb0}, + // Block 0x7, offset 0x8 + {value: 0x0005, lo: 0x02}, + {value: 0x0540, lo: 0x8a, hi: 0x8e}, + {value: 0x0562, lo: 0x93, hi: 0x94}, + // Block 0x8, offset 0x9 + {value: 0x0005, lo: 0x06}, + {value: 0x0584, lo: 0x80, hi: 0x81}, + {value: 0x058e, lo: 0x83, hi: 0x83}, + {value: 0x0593, lo: 0x87, hi: 0x87}, + {value: 0x0598, lo: 0x8c, hi: 0x8e}, + {value: 0x05a7, lo: 0x99, hi: 0x99}, + {value: 0x05ac, lo: 0xb9, hi: 0xb9}, + // Block 0x9, offset 0xa + {value: 0x0005, lo: 0x05}, + {value: 0x05b1, lo: 0x90, hi: 0x91}, + {value: 0x05bb, lo: 0x93, hi: 0x93}, + {value: 0x05c0, lo: 0x97, hi: 0x97}, + {value: 0x05c5, lo: 0x9c, hi: 0x9e}, + {value: 0x05d4, lo: 0xb6, hi: 0xb7}, + // Block 0xa, offset 0xb + {value: 0x0005, lo: 0x07}, + {value: 0x05de, lo: 0x81, hi: 0x82}, + {value: 0x05e8, lo: 0x90, hi: 0x93}, + {value: 0x05fc, lo: 0x96, hi: 0x97}, + {value: 0x0606, lo: 0x9a, hi: 0x9f}, + {value: 0x0624, lo: 0xa2, hi: 0xa7}, + {value: 0x0642, lo: 0xaa, hi: 0xb5}, + {value: 0x067e, lo: 0xb8, hi: 0xb9}, + // Block 0xb, offset 0xc + {value: 0x0005, lo: 0x01}, + {value: 0x068d, lo: 0xa2, hi: 0xa6}, + // Block 0xc, offset 0xd + {value: 0x0005, lo: 0x03}, + {value: 0x06ba, lo: 0x80, hi: 0x80}, + {value: 0x06bf, lo: 0x82, hi: 0x82}, + {value: 0x06c4, lo: 0x93, hi: 0x93}, + // Block 0xd, offset 0xe + {value: 0x0007, lo: 0x03}, + {value: 0x06c9, lo: 0xa9, hi: 0xa9}, + {value: 0x06d0, lo: 0xb1, hi: 0xb1}, + {value: 0x06d7, lo: 0xb4, hi: 0xb4}, + // Block 0xe, offset 0xf + {value: 0x0007, lo: 0x01}, + {value: 0x06de, lo: 0x98, hi: 0x9f}, + // Block 0xf, offset 0x10 + {value: 0x0007, lo: 0x03}, + {value: 0x0716, lo: 0x8b, hi: 0x8c}, + {value: 0x0724, lo: 0x9c, hi: 0x9d}, + {value: 0x0732, lo: 0x9f, hi: 0x9f}, + // Block 0x10, offset 0x11 + {value: 0x0007, lo: 0x02}, + {value: 0x0739, lo: 0xb3, hi: 0xb3}, + {value: 0x0740, lo: 0xb6, hi: 0xb6}, + // Block 0x11, offset 0x12 + {value: 0x0007, lo: 0x02}, + {value: 0x0747, lo: 0x99, hi: 0x9b}, + {value: 0x075c, lo: 0x9e, hi: 0x9e}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x03}, + {value: 0x0763, lo: 0x88, hi: 0x88}, + {value: 0x076a, lo: 0x8b, hi: 0x8c}, + {value: 0x0778, lo: 0x9c, hi: 0x9d}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x01}, + {value: 0x0786, lo: 0x94, hi: 0x94}, + // Block 0x14, offset 0x15 + {value: 0x0007, lo: 0x01}, + {value: 0x078d, lo: 0x8a, hi: 0x8c}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x01}, + {value: 0x07a2, lo: 0x88, hi: 0x88}, + // Block 0x16, offset 0x17 + {value: 0x0007, lo: 0x03}, + {value: 0x07a9, lo: 0x80, hi: 0x80}, + {value: 0x07b0, lo: 0x87, hi: 0x88}, + {value: 0x07be, lo: 0x8a, hi: 0x8b}, + // Block 0x17, offset 0x18 + {value: 0x0007, lo: 0x01}, + {value: 0x07cf, lo: 0x8a, hi: 0x8c}, + // Block 0x18, offset 0x19 + {value: 0x0007, lo: 0x03}, + {value: 0x07e4, lo: 0x9a, hi: 0x9a}, + {value: 0x07eb, lo: 0x9c, hi: 0x9d}, + {value: 0x07fc, lo: 0x9e, hi: 0x9e}, + // Block 0x19, offset 0x1a + {value: 0x0007, lo: 0x09}, + {value: 0x0823, lo: 0x83, hi: 0x83}, + {value: 0x082a, lo: 0x8d, hi: 0x8d}, + {value: 0x0831, lo: 0x92, hi: 0x92}, + {value: 0x0838, lo: 0x97, hi: 0x97}, + {value: 0x083f, lo: 0x9c, hi: 0x9c}, + {value: 0x0846, lo: 0xa9, hi: 0xa9}, + {value: 0x084d, lo: 0xb3, hi: 0xb3}, + {value: 0x0854, lo: 0xb5, hi: 0xb6}, + {value: 0x086c, lo: 0xb8, hi: 0xb8}, + // Block 0x1a, offset 0x1b + {value: 0x0007, lo: 0x07}, + {value: 0x087d, lo: 0x81, hi: 0x81}, + {value: 0x0884, lo: 0x93, hi: 0x93}, + {value: 0x088b, lo: 0x9d, hi: 0x9d}, + {value: 0x0892, lo: 0xa2, hi: 0xa2}, + {value: 0x0899, lo: 0xa7, hi: 0xa7}, + {value: 0x08a0, lo: 0xac, hi: 0xac}, + {value: 0x08a7, lo: 0xb9, hi: 0xb9}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x01}, + {value: 0x08ae, lo: 0xa6, hi: 0xa6}, + // Block 0x1c, offset 0x1d + {value: 0x0007, lo: 0x08}, + {value: 0x08b9, lo: 0x86, hi: 0x86}, + {value: 0x08c0, lo: 0x88, hi: 0x88}, + {value: 0x08c7, lo: 0x8a, hi: 0x8a}, + {value: 0x08ce, lo: 0x8c, hi: 0x8c}, + {value: 0x08d5, lo: 0x8e, hi: 0x8e}, + {value: 0x08dc, lo: 0x92, hi: 0x92}, + {value: 0x08e3, lo: 0xbb, hi: 0xbb}, + {value: 0x08ea, lo: 0xbd, hi: 0xbd}, + // Block 0x1d, offset 0x1e + {value: 0x0007, lo: 0x02}, + {value: 0x08f1, lo: 0x80, hi: 0x81}, + {value: 0x08ff, lo: 0x83, hi: 0x83}, + // Block 0x1e, offset 0x1f + {value: 0x0004, lo: 0x0d}, + {value: 0x09ea, lo: 0x80, hi: 0x88}, + {value: 0x0a10, lo: 0x89, hi: 0x89}, + {value: 0x0a16, lo: 0x8a, hi: 0x94}, + {value: 0x0a44, lo: 0x95, hi: 0x95}, + {value: 0x0a4a, lo: 0x96, hi: 0x96}, + {value: 0x0a50, lo: 0x97, hi: 0x97}, + {value: 0x0a56, lo: 0x98, hi: 0x9c}, + {value: 0x0a6c, lo: 0x9d, hi: 0x9d}, + {value: 0x0a72, lo: 0x9e, hi: 0xae}, + {value: 0x0ab8, lo: 0xaf, hi: 0xaf}, + {value: 0x0abe, lo: 0xb0, hi: 0xb8}, + {value: 0x0ae4, lo: 0xb9, hi: 0xb9}, + {value: 0x0aea, lo: 0xba, hi: 0xbf}, + // Block 0x1f, offset 0x20 + {value: 0x0004, lo: 0x01}, + {value: 0x142e, lo: 0x80, hi: 0x81}, + // Block 0x20, offset 0x21 + {value: 0x0000, lo: 0x03}, + {value: 0x14d6, lo: 0xa6, hi: 0xa6}, + {value: 0x091c, lo: 0xaa, hi: 0xaa}, + {value: 0x0046, lo: 0xab, hi: 0xab}, + // Block 0x21, offset 0x22 + {value: 0x0006, lo: 0x02}, + {value: 0x159f, lo: 0x9a, hi: 0x9b}, + {value: 0x15ab, lo: 0xae, hi: 0xae}, + // Block 0x22, offset 0x23 + {value: 0x0006, lo: 0x01}, + {value: 0x15b1, lo: 0x8d, hi: 0x8f}, + // Block 0x23, offset 0x24 + {value: 0x0006, lo: 0x05}, + {value: 0x15c3, lo: 0x84, hi: 0x84}, + {value: 0x15c9, lo: 0x89, hi: 0x89}, + {value: 0x15cf, lo: 0x8c, hi: 0x8c}, + {value: 0x15d5, lo: 0xa4, hi: 0xa4}, + {value: 0x15db, lo: 0xa6, hi: 0xa6}, + // Block 0x24, offset 0x25 + {value: 0x0006, lo: 0x0b}, + {value: 0x1603, lo: 0x81, hi: 0x81}, + {value: 0x1609, lo: 0x84, hi: 0x84}, + {value: 0x160f, lo: 0x87, hi: 0x87}, + {value: 0x1615, lo: 0x89, hi: 0x89}, + {value: 0x161b, lo: 0xa0, hi: 0xa0}, + {value: 0x161f, lo: 0xa2, hi: 0xa2}, + {value: 0x1625, lo: 0xad, hi: 0xae}, + {value: 0x162f, lo: 0xaf, hi: 0xaf}, + {value: 0x1633, lo: 0xb0, hi: 0xb1}, + {value: 0x163f, lo: 0xb4, hi: 0xb5}, + {value: 0x164b, lo: 0xb8, hi: 0xb9}, + // Block 0x25, offset 0x26 + {value: 0x0006, lo: 0x04}, + {value: 0x1657, lo: 0x80, hi: 0x81}, + {value: 0x1663, lo: 0x84, hi: 0x85}, + {value: 0x166f, lo: 0x88, hi: 0x89}, + {value: 0x167b, lo: 0xac, hi: 0xaf}, + // Block 0x26, offset 0x27 + {value: 0x0006, lo: 0x02}, + {value: 0x1693, lo: 0xa0, hi: 0xa3}, + {value: 0x16ab, lo: 0xaa, hi: 0xad}, + // Block 0x27, offset 0x28 + {value: 0x0004, lo: 0x01}, + {value: 0x16c3, lo: 0xa9, hi: 0xaa}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x01}, + {value: 0x1814, lo: 0x9c, hi: 0x9c}, + // Block 0x29, offset 0x2a + {value: 0x0007, lo: 0x0c}, + {value: 0x1c39, lo: 0x94, hi: 0x94}, + {value: 0x1c4a, lo: 0x9e, hi: 0x9e}, + {value: 0x1c58, lo: 0xac, hi: 0xac}, + {value: 0x1c5f, lo: 0xae, hi: 0xae}, + {value: 0x1c66, lo: 0xb0, hi: 0xb0}, + {value: 0x1c6d, lo: 0xb2, hi: 0xb2}, + {value: 0x1c74, lo: 0xb4, hi: 0xb4}, + {value: 0x1c7b, lo: 0xb6, hi: 0xb6}, + {value: 0x1c82, lo: 0xb8, hi: 0xb8}, + {value: 0x1c89, lo: 0xba, hi: 0xba}, + {value: 0x1c90, lo: 0xbc, hi: 0xbc}, + {value: 0x1c97, lo: 0xbe, hi: 0xbe}, + // Block 0x2a, offset 0x2b + {value: 0x0007, lo: 0x0d}, + {value: 0x1c9e, lo: 0x80, hi: 0x80}, + {value: 0x1ca5, lo: 0x82, hi: 0x82}, + {value: 0x1cac, lo: 0x85, hi: 0x85}, + {value: 0x1cb3, lo: 0x87, hi: 0x87}, + {value: 0x1cba, lo: 0x89, hi: 0x89}, + {value: 0x1cc1, lo: 0x90, hi: 0x91}, + {value: 0x1ccf, lo: 0x93, hi: 0x94}, + {value: 0x1cdd, lo: 0x96, hi: 0x97}, + {value: 0x1ceb, lo: 0x99, hi: 0x9a}, + {value: 0x1cf9, lo: 0x9c, hi: 0x9d}, + {value: 0x1d07, lo: 0xb4, hi: 0xb4}, + {value: 0x1d0e, lo: 0xb7, hi: 0xba}, + {value: 0x1d2a, lo: 0xbe, hi: 0xbe}, + // Block 0x2b, offset 0x2c + {value: 0x0004, lo: 0x0a}, + {value: 0x2a81, lo: 0x80, hi: 0x81}, + {value: 0x1a9e, lo: 0x82, hi: 0x82}, + {value: 0x2a89, lo: 0x83, hi: 0x86}, + {value: 0x1b76, lo: 0x87, hi: 0x87}, + {value: 0x1b76, lo: 0x88, hi: 0x88}, + {value: 0x2a99, lo: 0x89, hi: 0x89}, + {value: 0x1abe, lo: 0x8a, hi: 0x8a}, + {value: 0x2a9d, lo: 0x8b, hi: 0xb3}, + {value: 0x1a16, lo: 0xb4, hi: 0xb4}, + {value: 0x2b41, lo: 0xb5, hi: 0xbf}, + // Block 0x2c, offset 0x2d + {value: 0x0004, lo: 0x06}, + {value: 0x1b3a, lo: 0x80, hi: 0x80}, + {value: 0x2b6d, lo: 0x81, hi: 0x9b}, + {value: 0x2ac1, lo: 0x9c, hi: 0x9c}, + {value: 0x2bd9, lo: 0x9d, hi: 0xb0}, + {value: 0x1aa6, lo: 0xb1, hi: 0xb1}, + {value: 0x2c29, lo: 0xb2, hi: 0xbf}, + // Block 0x2d, offset 0x2e + {value: 0x0004, lo: 0x0a}, + {value: 0x2c61, lo: 0x80, hi: 0x80}, + {value: 0x18ba, lo: 0x81, hi: 0x81}, + {value: 0x2c65, lo: 0x82, hi: 0x89}, + {value: 0x186e, lo: 0x8a, hi: 0x8a}, + {value: 0x2c85, lo: 0x8b, hi: 0xa0}, + {value: 0x2c21, lo: 0xa1, hi: 0xa1}, + {value: 0x2cdd, lo: 0xa2, hi: 0xa9}, + {value: 0x2be1, lo: 0xaa, hi: 0xaa}, + {value: 0x2cfd, lo: 0xab, hi: 0xbe}, + {value: 0x2ac1, lo: 0xbf, hi: 0xbf}, + // Block 0x2e, offset 0x2f + {value: 0x0004, lo: 0x0b}, + {value: 0x2d4d, lo: 0x80, hi: 0x83}, + {value: 0x1b72, lo: 0x84, hi: 0x84}, + {value: 0x2d5d, lo: 0x85, hi: 0x90}, + {value: 0x2173, lo: 0x91, hi: 0x91}, + {value: 0x2d8d, lo: 0x92, hi: 0x9a}, + {value: 0x2be9, lo: 0x9b, hi: 0x9b}, + {value: 0x2db1, lo: 0x9c, hi: 0xa8}, + {value: 0x1aba, lo: 0xa9, hi: 0xa9}, + {value: 0x2de5, lo: 0xaa, hi: 0xb6}, + {value: 0x19f6, lo: 0xb7, hi: 0xb7}, + {value: 0x2e19, lo: 0xb8, hi: 0xbf}, + // Block 0x2f, offset 0x30 + {value: 0x0004, lo: 0x10}, + {value: 0x2e39, lo: 0x80, hi: 0x87}, + {value: 0x1a62, lo: 0x88, hi: 0x88}, + {value: 0x2e59, lo: 0x89, hi: 0x89}, + {value: 0x1a6e, lo: 0x8a, hi: 0x8a}, + {value: 0x2e5d, lo: 0x8b, hi: 0x8d}, + {value: 0x2e69, lo: 0x90, hi: 0x90}, + {value: 0x2e6d, lo: 0x92, hi: 0x92}, + {value: 0x2e71, lo: 0x95, hi: 0x9d}, + {value: 0x1a12, lo: 0x9e, hi: 0x9e}, + {value: 0x2e95, lo: 0xa0, hi: 0xa0}, + {value: 0x2e99, lo: 0xa2, hi: 0xa2}, + {value: 0x2e9d, lo: 0xa5, hi: 0xa6}, + {value: 0x2ea5, lo: 0xaa, hi: 0xad}, + {value: 0x2eb5, lo: 0xb0, hi: 0xbb}, + {value: 0x18d6, lo: 0xbc, hi: 0xbc}, + {value: 0x2ee5, lo: 0xbd, hi: 0xbf}, + // Block 0x30, offset 0x31 + {value: 0x0004, lo: 0x10}, + {value: 0x2ef1, lo: 0x80, hi: 0x8b}, + {value: 0x2187, lo: 0x8c, hi: 0x8c}, + {value: 0x2f21, lo: 0x8d, hi: 0x90}, + {value: 0x2197, lo: 0x91, hi: 0x91}, + {value: 0x2f31, lo: 0x92, hi: 0x96}, + {value: 0x2cb1, lo: 0x97, hi: 0x97}, + {value: 0x2f45, lo: 0x98, hi: 0x9d}, + {value: 0x2f59, lo: 0x9e, hi: 0xa6}, + {value: 0x2e9d, lo: 0xa7, hi: 0xa7}, + {value: 0x2f7d, lo: 0xa8, hi: 0xac}, + {value: 0x2f92, lo: 0xad, hi: 0xad}, + {value: 0x2f96, lo: 0xb0, hi: 0xb7}, + {value: 0x2ecd, lo: 0xb8, hi: 0xb8}, + {value: 0x2fb6, lo: 0xb9, hi: 0xbb}, + {value: 0x2e69, lo: 0xbc, hi: 0xbc}, + {value: 0x2fc2, lo: 0xbd, hi: 0xbf}, + // Block 0x31, offset 0x32 + {value: 0x0005, lo: 0x07}, + {value: 0x3105, lo: 0x9d, hi: 0x9d}, + {value: 0x310a, lo: 0x9f, hi: 0x9f}, + {value: 0x3124, lo: 0xaa, hi: 0xac}, + {value: 0x3135, lo: 0xad, hi: 0xad}, + {value: 0x313c, lo: 0xae, hi: 0xb6}, + {value: 0x3169, lo: 0xb8, hi: 0xbc}, + {value: 0x3182, lo: 0xbe, hi: 0xbe}, + // Block 0x32, offset 0x33 + {value: 0x0005, lo: 0x03}, + {value: 0x3187, lo: 0x80, hi: 0x81}, + {value: 0x3191, lo: 0x83, hi: 0x84}, + {value: 0x319b, lo: 0x86, hi: 0x8e}, + // Block 0x33, offset 0x34 + {value: 0x0009, lo: 0x03}, + {value: 0x3a73, lo: 0x9a, hi: 0x9a}, + {value: 0x3a7c, lo: 0x9c, hi: 0x9c}, + {value: 0x3a85, lo: 0xab, hi: 0xab}, + // Block 0x34, offset 0x35 + {value: 0x000d, lo: 0x03}, + {value: 0x3a8e, lo: 0x9e, hi: 0x9e}, + {value: 0x3a97, lo: 0x9f, hi: 0x9f}, + {value: 0x3aa0, lo: 0xa0, hi: 0xa4}, + // Block 0x35, offset 0x36 + {value: 0x0009, lo: 0x03}, + {value: 0x3ae1, lo: 0xbb, hi: 0xbd}, + {value: 0x3b00, lo: 0xbe, hi: 0xbe}, + {value: 0x3b0d, lo: 0xbf, hi: 0xbf}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x3b1a, lo: 0x80, hi: 0x80}, + // Block 0x37, offset 0x38 + {value: 0x0004, lo: 0x0d}, + {value: 0x4463, lo: 0x80, hi: 0x81}, + {value: 0x446c, lo: 0x82, hi: 0x89}, + {value: 0x30a2, lo: 0x8a, hi: 0x8a}, + {value: 0x448d, lo: 0x8b, hi: 0x90}, + {value: 0x44a6, lo: 0x91, hi: 0x92}, + {value: 0x44af, lo: 0x93, hi: 0x93}, + {value: 0x44b4, lo: 0x94, hi: 0x94}, + {value: 0x1b42, lo: 0x95, hi: 0x95}, + {value: 0x44b9, lo: 0x96, hi: 0x96}, + {value: 0x1b52, lo: 0x97, hi: 0x97}, + {value: 0x44bd, lo: 0x98, hi: 0x9b}, + {value: 0x1b66, lo: 0x9c, hi: 0x9c}, + {value: 0x44cd, lo: 0x9d, hi: 0x9d}, } // nfcDecompLookup: 832 bytes @@ -3038,37 +3125,37 @@ var nfcDecompLookup = [832]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c3: 0x03, 0x0c4: 0x04, 0x0c5: 0x05, 0x0c6: 0x06, 0x0c7: 0x07, - 0x0c8: 0x08, 0x0cd: 0x09, 0x0ce: 0x0a, 0x0cf: 0x0b, - 0x0d0: 0x0c, 0x0d1: 0x0d, 0x0d3: 0x0e, - 0x0d8: 0x0f, 0x0db: 0x10, + 0x0c3: 0x16, 0x0c4: 0x17, 0x0c5: 0x18, 0x0c6: 0x19, 0x0c7: 0x03, + 0x0c8: 0x1a, 0x0cd: 0x1b, 0x0ce: 0x1c, 0x0cf: 0x1d, + 0x0d0: 0x1e, 0x0d1: 0x1f, 0x0d3: 0x20, + 0x0d8: 0x21, 0x0db: 0x22, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ef: 0x08, 0x0f0: 0x0c, // Block 0x4, offset 0x100 - 0x124: 0x11, 0x125: 0x12, 0x127: 0x13, - 0x128: 0x14, 0x129: 0x15, 0x12d: 0x16, 0x12e: 0x17, 0x12f: 0x18, - 0x131: 0x19, 0x133: 0x1a, 0x135: 0x1b, 0x137: 0x1c, - 0x13d: 0x1d, 0x13e: 0x1e, + 0x124: 0x23, 0x125: 0x24, 0x127: 0x25, + 0x128: 0x26, 0x129: 0x27, 0x12d: 0x28, 0x12e: 0x29, 0x12f: 0x2a, + 0x131: 0x2b, 0x133: 0x2c, 0x135: 0x2d, 0x137: 0x2e, + 0x13d: 0x2f, 0x13e: 0x30, // Block 0x5, offset 0x140 - 0x140: 0x1f, - 0x16c: 0x20, 0x16d: 0x21, - 0x178: 0x22, 0x179: 0x23, 0x17a: 0x24, 0x17b: 0x25, 0x17c: 0x26, 0x17d: 0x27, 0x17e: 0x28, 0x17f: 0x29, + 0x140: 0x31, + 0x16c: 0x32, 0x16d: 0x33, + 0x178: 0x34, 0x179: 0x04, 0x17a: 0x05, 0x17b: 0x06, 0x17c: 0x07, 0x17d: 0x08, 0x17e: 0x09, 0x17f: 0x0a, // Block 0x6, offset 0x180 - 0x180: 0x2a, 0x184: 0x2b, 0x186: 0x2c, 0x187: 0x2d, - 0x188: 0x2e, 0x189: 0x2f, 0x18a: 0x30, 0x18b: 0x31, 0x18c: 0x32, - 0x1ab: 0x33, + 0x180: 0x35, 0x184: 0x36, 0x186: 0x37, 0x187: 0x38, + 0x188: 0x39, 0x189: 0x3a, 0x18a: 0x3b, 0x18b: 0x3c, 0x18c: 0x3d, + 0x1ab: 0x3e, // Block 0x7, offset 0x1c0 - 0x1c1: 0x34, 0x1c2: 0x35, 0x1c3: 0x36, + 0x1c1: 0x0b, 0x1c2: 0x3f, 0x1c3: 0x40, // Block 0x8, offset 0x200 - 0x224: 0x37, 0x225: 0x38, 0x226: 0x39, 0x227: 0x3a, - 0x228: 0x3b, 0x229: 0x3c, 0x22a: 0x3d, 0x22b: 0x3e, 0x22c: 0x3f, 0x22d: 0x40, + 0x224: 0x41, 0x225: 0x42, 0x226: 0x43, 0x227: 0x44, + 0x228: 0x45, 0x229: 0x46, 0x22a: 0x0c, 0x22b: 0x0d, 0x22c: 0x47, 0x22d: 0x48, // Block 0x9, offset 0x240 - 0x242: 0x41, + 0x242: 0x49, // Block 0xa, offset 0x280 - 0x285: 0x42, 0x286: 0x43, 0x287: 0x44, + 0x285: 0x4a, 0x286: 0x4b, 0x287: 0x4c, // Block 0xb, offset 0x2c0 - 0x2e0: 0x45, 0x2e1: 0x46, 0x2e2: 0x47, 0x2e3: 0x48, 0x2e4: 0x49, 0x2e5: 0x4a, 0x2e6: 0x4b, 0x2e7: 0x4c, + 0x2e0: 0x0e, 0x2e1: 0x0f, 0x2e2: 0x10, 0x2e3: 0x11, 0x2e4: 0x12, 0x2e5: 0x13, 0x2e6: 0x14, 0x2e7: 0x15, 0x2e8: 0x4d, // Block 0xc, offset 0x300 0x311: 0x09, @@ -3076,1288 +3163,1442 @@ var nfcDecompLookup = [832]uint8{ 0x32f: 0x0b, } -var nfcDecompTrie = trie{nfcDecompLookup[:], nfcDecompValues[:]} +var nfcDecompTrie = trie{nfcDecompLookup[:], nfcDecompValues[:], nfcDecompSparseValues[:], nfcDecompSparseOffset[:], 22} -// nfkcDecompValues: 10176 entries, 20352 bytes +// nfkcDecompValues: 4224 entries, 8448 bytes // Block 2 is the null block. -var nfkcDecompValues = [10176]uint16{ +var nfkcDecompValues = [4224]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00e0: 0x0001, - 0x00e8: 0x0003, - 0x00ea: 0x0007, 0x00ef: 0x0009, - 0x00f2: 0x000d, 0x00f3: 0x000f, 0x00f4: 0x0011, 0x00f5: 0x0015, - 0x00f8: 0x0018, 0x00f9: 0x001c, 0x00fa: 0x001e, - 0x00fc: 0x0020, 0x00fd: 0x0026, 0x00fe: 0x002c, + 0x00c4: 0x02da, 0x00c5: 0x02df, + 0x00c6: 0x02e4, 0x00c7: 0x02e9, 0x00c8: 0x02ec, 0x00c9: 0x02ef, 0x00ca: 0x02f2, 0x00cb: 0x02f5, + 0x00cc: 0x02f8, 0x00cd: 0x02fb, 0x00ce: 0x02ff, 0x00cf: 0x0303, 0x00d0: 0x0307, 0x00d1: 0x030b, + 0x00d2: 0x030f, 0x00d3: 0x0313, 0x00d4: 0x0317, 0x00d5: 0x031b, 0x00d6: 0x0321, 0x00d7: 0x0327, + 0x00d8: 0x032d, 0x00d9: 0x0333, 0x00da: 0x0339, 0x00db: 0x033f, 0x00dc: 0x0345, + 0x00de: 0x034b, 0x00df: 0x0351, 0x00e0: 0x0357, 0x00e1: 0x035d, 0x00e2: 0x0363, 0x00e3: 0x0368, + 0x00e6: 0x036d, 0x00e7: 0x0371, 0x00e8: 0x0375, 0x00e9: 0x0379, + 0x00ea: 0x037d, 0x00eb: 0x0381, 0x00ec: 0x0385, 0x00ed: 0x038b, 0x00ee: 0x0391, 0x00ef: 0x0396, + 0x00f0: 0x039b, 0x00f1: 0x039f, 0x00f2: 0x03a2, 0x00f3: 0x03a5, 0x00f4: 0x03a8, 0x00f5: 0x03ac, + 0x00f8: 0x03b0, 0x00f9: 0x03b4, 0x00fa: 0x03b8, 0x00fb: 0x03be, + 0x00fc: 0x03c4, 0x00fd: 0x03c9, 0x00fe: 0x03ce, 0x00ff: 0x03d3, // Block 0x4, offset 0x100 - 0x0100: 0x0032, 0x0101: 0x0036, 0x0102: 0x003a, 0x0103: 0x003e, 0x0104: 0x0042, 0x0105: 0x0046, - 0x0107: 0x004a, 0x0108: 0x004e, 0x0109: 0x0052, 0x010a: 0x0056, 0x010b: 0x005a, - 0x010c: 0x005e, 0x010d: 0x0062, 0x010e: 0x0066, 0x010f: 0x006a, 0x0111: 0x006e, - 0x0112: 0x0072, 0x0113: 0x0076, 0x0114: 0x007a, 0x0115: 0x007e, 0x0116: 0x0082, - 0x0119: 0x0086, 0x011a: 0x008a, 0x011b: 0x008e, 0x011c: 0x0092, 0x011d: 0x0096, - 0x0120: 0x009a, 0x0121: 0x009e, 0x0122: 0x00a2, 0x0123: 0x00a6, - 0x0124: 0x00aa, 0x0125: 0x00ae, 0x0127: 0x00b2, 0x0128: 0x00b6, 0x0129: 0x00ba, - 0x012a: 0x00be, 0x012b: 0x00c2, 0x012c: 0x00c6, 0x012d: 0x00ca, 0x012e: 0x00ce, 0x012f: 0x00d2, - 0x0131: 0x00d6, 0x0132: 0x00da, 0x0133: 0x00de, 0x0134: 0x00e2, 0x0135: 0x00e6, - 0x0136: 0x00ea, 0x0139: 0x00ee, 0x013a: 0x00f2, 0x013b: 0x00f6, - 0x013c: 0x00fa, 0x013d: 0x00fe, 0x013f: 0x0102, + 0x0100: 0x092d, 0x0101: 0x092f, 0x0102: 0x0931, 0x0103: 0x0007, 0x0104: 0x0933, 0x0105: 0x0936, + 0x0106: 0x0939, 0x0107: 0x093d, 0x0108: 0x093f, 0x0109: 0x0941, 0x010a: 0x0943, 0x010b: 0x0946, + 0x010c: 0x0949, 0x010d: 0x094c, 0x010f: 0x094e, 0x0110: 0x0950, 0x0111: 0x0952, + 0x0112: 0x001e, 0x0113: 0x0955, 0x0114: 0x0958, 0x0115: 0x095c, 0x0116: 0x0960, 0x0117: 0x0962, + 0x0118: 0x0964, 0x0119: 0x0966, 0x011a: 0x096a, 0x011b: 0x096d, 0x011c: 0x096f, 0x011d: 0x0559, + 0x011e: 0x0973, 0x011f: 0x0976, 0x0120: 0x056c, 0x0121: 0x0979, 0x0122: 0x097c, 0x0123: 0x049b, + 0x0124: 0x0964, 0x0125: 0x096d, 0x0126: 0x0559, 0x0127: 0x0973, 0x0128: 0x0575, 0x0129: 0x056c, + 0x012a: 0x0979, + 0x0138: 0x097e, // Block 0x5, offset 0x140 - 0x0140: 0x0106, 0x0141: 0x010a, 0x0142: 0x010e, 0x0143: 0x0112, 0x0144: 0x0116, 0x0145: 0x011a, - 0x0146: 0x011e, 0x0147: 0x0122, 0x0148: 0x0126, 0x0149: 0x012a, 0x014a: 0x012e, 0x014b: 0x0132, - 0x014c: 0x0136, 0x014d: 0x013a, 0x014e: 0x013e, 0x014f: 0x0142, - 0x0152: 0x0146, 0x0153: 0x014a, 0x0154: 0x014e, 0x0155: 0x0152, 0x0156: 0x0156, 0x0157: 0x015a, - 0x0158: 0x015e, 0x0159: 0x0162, 0x015a: 0x0166, 0x015b: 0x016a, 0x015c: 0x016e, 0x015d: 0x0172, - 0x015e: 0x0176, 0x015f: 0x017a, 0x0160: 0x017e, 0x0161: 0x0182, 0x0162: 0x0186, 0x0163: 0x018a, - 0x0164: 0x018e, 0x0165: 0x0192, 0x0168: 0x0196, 0x0169: 0x019a, - 0x016a: 0x019e, 0x016b: 0x01a2, 0x016c: 0x01a6, 0x016d: 0x01aa, 0x016e: 0x01ae, 0x016f: 0x01b2, - 0x0170: 0x01b6, 0x0172: 0x01ba, 0x0173: 0x01bd, 0x0174: 0x01c0, 0x0175: 0x01c4, - 0x0176: 0x01c8, 0x0177: 0x01cc, 0x0179: 0x01d0, 0x017a: 0x01d4, 0x017b: 0x01d8, - 0x017c: 0x01dc, 0x017d: 0x01e0, 0x017e: 0x01e4, 0x017f: 0x01e8, + 0x0140: 0x0b02, 0x0141: 0x0b06, 0x0142: 0x0b0a, 0x0143: 0x0b0e, 0x0144: 0x0b12, 0x0145: 0x0b16, + 0x0146: 0x0b1a, 0x0147: 0x0b1e, 0x0148: 0x0b22, 0x0149: 0x0b26, 0x014a: 0x0b2a, 0x014b: 0x0b2e, + 0x014c: 0x0b32, 0x014d: 0x0b38, 0x014e: 0x0b3e, 0x014f: 0x0b44, 0x0150: 0x0b4a, 0x0151: 0x0b50, + 0x0152: 0x0b56, 0x0153: 0x0b5c, 0x0154: 0x0b62, 0x0155: 0x0b66, 0x0156: 0x0b6a, 0x0157: 0x0b6e, + 0x0158: 0x0b72, 0x0159: 0x0b76, 0x015a: 0x0b7a, 0x015b: 0x0b7e, 0x015c: 0x0b82, 0x015d: 0x0b88, + 0x015e: 0x0b8e, 0x015f: 0x0b92, 0x0160: 0x0b96, 0x0161: 0x0b9a, 0x0162: 0x0b9e, 0x0163: 0x0ba2, + 0x0164: 0x0ba6, 0x0165: 0x0bac, 0x0166: 0x0bb2, 0x0167: 0x0bb8, 0x0168: 0x0bbe, 0x0169: 0x0bc4, + 0x016a: 0x0bca, 0x016b: 0x0bce, 0x016c: 0x0bd2, 0x016d: 0x0bd6, 0x016e: 0x0bda, 0x016f: 0x0bde, + 0x0170: 0x0be2, 0x0171: 0x0be6, 0x0172: 0x0bea, 0x0173: 0x0bee, 0x0174: 0x0bf2, 0x0175: 0x0bf6, + 0x0176: 0x0bfa, 0x0177: 0x0bfe, 0x0178: 0x0c02, 0x0179: 0x0c08, 0x017a: 0x0c0e, 0x017b: 0x0c14, + 0x017c: 0x0c1a, 0x017d: 0x0c1e, 0x017e: 0x0c22, 0x017f: 0x0c26, // Block 0x6, offset 0x180 - 0x0180: 0x01ec, 0x0183: 0x01f0, 0x0184: 0x01f4, 0x0185: 0x01f8, - 0x0186: 0x01fc, 0x0187: 0x0200, 0x0188: 0x0204, 0x0189: 0x0208, - 0x018c: 0x020c, 0x018d: 0x0210, 0x018e: 0x0214, 0x018f: 0x0218, 0x0190: 0x021c, 0x0191: 0x0220, - 0x0194: 0x0224, 0x0195: 0x0228, 0x0196: 0x022c, 0x0197: 0x0230, - 0x0198: 0x0234, 0x0199: 0x0238, 0x019a: 0x023c, 0x019b: 0x0240, 0x019c: 0x0244, 0x019d: 0x0248, - 0x019e: 0x024c, 0x019f: 0x0250, 0x01a0: 0x0254, 0x01a1: 0x0258, 0x01a2: 0x025c, 0x01a3: 0x0260, - 0x01a4: 0x0264, 0x01a5: 0x0268, 0x01a8: 0x026c, 0x01a9: 0x0270, - 0x01aa: 0x0274, 0x01ab: 0x0278, 0x01ac: 0x027c, 0x01ad: 0x0280, 0x01ae: 0x0284, 0x01af: 0x0288, - 0x01b0: 0x028c, 0x01b1: 0x0290, 0x01b2: 0x0294, 0x01b3: 0x0298, 0x01b4: 0x029c, 0x01b5: 0x02a0, - 0x01b6: 0x02a4, 0x01b7: 0x02a8, 0x01b8: 0x02ac, 0x01b9: 0x02b0, 0x01ba: 0x02b4, 0x01bb: 0x02b8, - 0x01bc: 0x02bc, 0x01bd: 0x02c0, 0x01be: 0x02c4, 0x01bf: 0x02c8, + 0x0180: 0x0c2a, 0x0181: 0x0c2e, 0x0182: 0x0c32, 0x0183: 0x0c36, 0x0184: 0x0c3a, 0x0185: 0x0c3e, + 0x0186: 0x0c42, 0x0187: 0x0c46, 0x0188: 0x0c4a, 0x0189: 0x0c4e, 0x018a: 0x0c52, 0x018b: 0x0c56, + 0x018c: 0x0c5a, 0x018d: 0x0c5e, 0x018e: 0x0c62, 0x018f: 0x0c66, 0x0190: 0x0c6a, 0x0191: 0x0c6e, + 0x0192: 0x0c72, 0x0193: 0x0c76, 0x0194: 0x0c7a, 0x0195: 0x0c7e, 0x0196: 0x0c82, 0x0197: 0x0c86, + 0x0198: 0x0c8a, 0x0199: 0x0c8e, 0x019a: 0x0c92, 0x019b: 0x0b9a, + 0x01a0: 0x0c9b, 0x01a1: 0x0c9f, 0x01a2: 0x0ca3, 0x01a3: 0x0ca7, + 0x01a4: 0x0cab, 0x01a5: 0x0cb1, 0x01a6: 0x0cb7, 0x01a7: 0x0cbd, 0x01a8: 0x0cc3, 0x01a9: 0x0cc9, + 0x01aa: 0x0ccf, 0x01ab: 0x0cd5, 0x01ac: 0x0cdb, 0x01ad: 0x0ce1, 0x01ae: 0x0ce7, 0x01af: 0x0ced, + 0x01b0: 0x0cf3, 0x01b1: 0x0cf9, 0x01b2: 0x0cff, 0x01b3: 0x0d05, 0x01b4: 0x0d0b, 0x01b5: 0x0d11, + 0x01b6: 0x0d17, 0x01b7: 0x0d1d, 0x01b8: 0x0d23, 0x01b9: 0x0d27, 0x01ba: 0x0d2b, 0x01bb: 0x0d2f, + 0x01bc: 0x0d33, 0x01bd: 0x0d37, 0x01be: 0x0d3b, 0x01bf: 0x0d41, // Block 0x7, offset 0x1c0 - 0x01e0: 0x02ca, 0x01e1: 0x02ce, - 0x01ef: 0x02d2, - 0x01f0: 0x02d6, + 0x01c0: 0x0d47, 0x01c1: 0x0d4d, 0x01c2: 0x0d53, 0x01c3: 0x0d59, 0x01c4: 0x0d5f, 0x01c5: 0x0d65, + 0x01c6: 0x0d6b, 0x01c7: 0x0d71, 0x01c8: 0x0d77, 0x01c9: 0x0d7b, 0x01ca: 0x0d7f, 0x01cb: 0x0d83, + 0x01cc: 0x0d87, 0x01cd: 0x0d8b, 0x01ce: 0x0d8f, 0x01cf: 0x0d93, 0x01d0: 0x0d97, 0x01d1: 0x0d9d, + 0x01d2: 0x0da3, 0x01d3: 0x0da9, 0x01d4: 0x0daf, 0x01d5: 0x0db5, 0x01d6: 0x0dbb, 0x01d7: 0x0dc1, + 0x01d8: 0x0dc7, 0x01d9: 0x0dcd, 0x01da: 0x0dd3, 0x01db: 0x0dd9, 0x01dc: 0x0ddf, 0x01dd: 0x0de5, + 0x01de: 0x0deb, 0x01df: 0x0df1, 0x01e0: 0x0df7, 0x01e1: 0x0dfd, 0x01e2: 0x0e03, 0x01e3: 0x0e09, + 0x01e4: 0x0e0f, 0x01e5: 0x0e13, 0x01e6: 0x0e17, 0x01e7: 0x0e1b, 0x01e8: 0x0e1f, 0x01e9: 0x0e25, + 0x01ea: 0x0e2b, 0x01eb: 0x0e31, 0x01ec: 0x0e37, 0x01ed: 0x0e3d, 0x01ee: 0x0e43, 0x01ef: 0x0e49, + 0x01f0: 0x0e4f, 0x01f1: 0x0e55, 0x01f2: 0x0e5b, 0x01f3: 0x0e5f, 0x01f4: 0x0e63, 0x01f5: 0x0e67, + 0x01f6: 0x0e6b, 0x01f7: 0x0e6f, 0x01f8: 0x0e73, 0x01f9: 0x0e77, // Block 0x8, offset 0x200 - 0x0204: 0x02da, 0x0205: 0x02df, - 0x0206: 0x02e4, 0x0207: 0x02e9, 0x0208: 0x02ec, 0x0209: 0x02ef, 0x020a: 0x02f2, 0x020b: 0x02f5, - 0x020c: 0x02f8, 0x020d: 0x02fb, 0x020e: 0x02ff, 0x020f: 0x0303, 0x0210: 0x0307, 0x0211: 0x030b, - 0x0212: 0x030f, 0x0213: 0x0313, 0x0214: 0x0317, 0x0215: 0x031b, 0x0216: 0x0321, 0x0217: 0x0327, - 0x0218: 0x032d, 0x0219: 0x0333, 0x021a: 0x0339, 0x021b: 0x033f, 0x021c: 0x0345, - 0x021e: 0x034b, 0x021f: 0x0351, 0x0220: 0x0357, 0x0221: 0x035d, 0x0222: 0x0363, 0x0223: 0x0368, - 0x0226: 0x036d, 0x0227: 0x0371, 0x0228: 0x0375, 0x0229: 0x0379, - 0x022a: 0x037d, 0x022b: 0x0381, 0x022c: 0x0385, 0x022d: 0x038b, 0x022e: 0x0391, 0x022f: 0x0396, - 0x0230: 0x039b, 0x0231: 0x039f, 0x0232: 0x03a2, 0x0233: 0x03a5, 0x0234: 0x03a8, 0x0235: 0x03ac, - 0x0238: 0x03b0, 0x0239: 0x03b4, 0x023a: 0x03b8, 0x023b: 0x03be, - 0x023c: 0x03c4, 0x023d: 0x03c9, 0x023e: 0x03ce, 0x023f: 0x03d3, + 0x0200: 0x0e7b, 0x0201: 0x0e80, 0x0202: 0x0e85, 0x0203: 0x0e8c, 0x0204: 0x0e93, 0x0205: 0x0e9a, + 0x0206: 0x0ea1, 0x0207: 0x0ea8, 0x0208: 0x0eaf, 0x0209: 0x0eb4, 0x020a: 0x0eb9, 0x020b: 0x0ec0, + 0x020c: 0x0ec7, 0x020d: 0x0ece, 0x020e: 0x0ed5, 0x020f: 0x0edc, 0x0210: 0x0ee3, 0x0211: 0x0ee8, + 0x0212: 0x0eed, 0x0213: 0x0ef4, 0x0214: 0x0efb, 0x0215: 0x0f02, + 0x0218: 0x0f09, 0x0219: 0x0f0e, 0x021a: 0x0f13, 0x021b: 0x0f1a, 0x021c: 0x0f21, 0x021d: 0x0f28, + 0x0220: 0x0f2f, 0x0221: 0x0f34, 0x0222: 0x0f39, 0x0223: 0x0f40, + 0x0224: 0x0f47, 0x0225: 0x0f4e, 0x0226: 0x0f55, 0x0227: 0x0f5c, 0x0228: 0x0f63, 0x0229: 0x0f68, + 0x022a: 0x0f6d, 0x022b: 0x0f74, 0x022c: 0x0f7b, 0x022d: 0x0f82, 0x022e: 0x0f89, 0x022f: 0x0f90, + 0x0230: 0x0f97, 0x0231: 0x0f9c, 0x0232: 0x0fa1, 0x0233: 0x0fa8, 0x0234: 0x0faf, 0x0235: 0x0fb6, + 0x0236: 0x0fbd, 0x0237: 0x0fc4, 0x0238: 0x0fcb, 0x0239: 0x0fd0, 0x023a: 0x0fd5, 0x023b: 0x0fdc, + 0x023c: 0x0fe3, 0x023d: 0x0fea, 0x023e: 0x0ff1, 0x023f: 0x0ff8, // Block 0x9, offset 0x240 - 0x0240: 0x03d8, 0x0241: 0x03dc, 0x0242: 0x03e0, 0x0243: 0x03e4, 0x0244: 0x03e8, 0x0245: 0x03ec, - 0x0246: 0x03f0, 0x0247: 0x03f4, 0x0248: 0x03f8, 0x0249: 0x03fc, 0x024a: 0x0400, 0x024b: 0x0404, - 0x024c: 0x0408, 0x024d: 0x040c, 0x024e: 0x0410, 0x024f: 0x0414, 0x0250: 0x0418, 0x0251: 0x041c, - 0x0252: 0x0420, 0x0253: 0x0424, 0x0254: 0x0428, 0x0255: 0x042c, 0x0256: 0x0430, 0x0257: 0x0434, - 0x0258: 0x0438, 0x0259: 0x043c, 0x025a: 0x0440, 0x025b: 0x0444, - 0x025e: 0x0448, 0x025f: 0x044c, - 0x0266: 0x0450, 0x0267: 0x0454, 0x0268: 0x0458, 0x0269: 0x045c, - 0x026a: 0x0460, 0x026b: 0x0466, 0x026c: 0x046c, 0x026d: 0x0472, 0x026e: 0x0478, 0x026f: 0x047c, - 0x0270: 0x0480, 0x0271: 0x0486, 0x0272: 0x048c, 0x0273: 0x0490, + 0x0240: 0x0fff, 0x0241: 0x1004, 0x0242: 0x1009, 0x0243: 0x1010, 0x0244: 0x1017, 0x0245: 0x101e, + 0x0248: 0x1025, 0x0249: 0x102a, 0x024a: 0x102f, 0x024b: 0x1036, + 0x024c: 0x103d, 0x024d: 0x1044, 0x0250: 0x104b, 0x0251: 0x1050, + 0x0252: 0x1055, 0x0253: 0x105c, 0x0254: 0x1063, 0x0255: 0x106a, 0x0256: 0x1071, 0x0257: 0x1078, + 0x0259: 0x107f, 0x025b: 0x1084, 0x025d: 0x108b, + 0x025f: 0x1092, 0x0260: 0x1099, 0x0261: 0x109e, 0x0262: 0x10a3, 0x0263: 0x10aa, + 0x0264: 0x10b1, 0x0265: 0x10b8, 0x0266: 0x10bf, 0x0267: 0x10c6, 0x0268: 0x10cd, 0x0269: 0x10d2, + 0x026a: 0x10d7, 0x026b: 0x10de, 0x026c: 0x10e5, 0x026d: 0x10ec, 0x026e: 0x10f3, 0x026f: 0x10fa, + 0x0270: 0x1101, 0x0271: 0x0525, 0x0272: 0x1106, 0x0273: 0x052a, 0x0274: 0x110b, 0x0275: 0x052f, + 0x0276: 0x1110, 0x0277: 0x0534, 0x0278: 0x1115, 0x0279: 0x054a, 0x027a: 0x111a, 0x027b: 0x054f, + 0x027c: 0x111f, 0x027d: 0x0554, // Block 0xa, offset 0x280 - 0x02b0: 0x0494, 0x02b1: 0x0496, 0x02b2: 0x0499, 0x02b3: 0x049b, 0x02b4: 0x049d, 0x02b5: 0x04a0, - 0x02b6: 0x04a3, 0x02b7: 0x04a6, 0x02b8: 0x04a8, + 0x0280: 0x1124, 0x0281: 0x112b, 0x0282: 0x1132, 0x0283: 0x113b, 0x0284: 0x1144, 0x0285: 0x114d, + 0x0286: 0x1156, 0x0287: 0x115f, 0x0288: 0x1168, 0x0289: 0x116f, 0x028a: 0x1176, 0x028b: 0x117f, + 0x028c: 0x1188, 0x028d: 0x1191, 0x028e: 0x119a, 0x028f: 0x11a3, 0x0290: 0x11ac, 0x0291: 0x11b3, + 0x0292: 0x11ba, 0x0293: 0x11c3, 0x0294: 0x11cc, 0x0295: 0x11d5, 0x0296: 0x11de, 0x0297: 0x11e7, + 0x0298: 0x11f0, 0x0299: 0x11f7, 0x029a: 0x11fe, 0x029b: 0x1207, 0x029c: 0x1210, 0x029d: 0x1219, + 0x029e: 0x1222, 0x029f: 0x122b, 0x02a0: 0x1234, 0x02a1: 0x123b, 0x02a2: 0x1242, 0x02a3: 0x124b, + 0x02a4: 0x1254, 0x02a5: 0x125d, 0x02a6: 0x1266, 0x02a7: 0x126f, 0x02a8: 0x1278, 0x02a9: 0x127f, + 0x02aa: 0x1286, 0x02ab: 0x128f, 0x02ac: 0x1298, 0x02ad: 0x12a1, 0x02ae: 0x12aa, 0x02af: 0x12b3, + 0x02b0: 0x12bc, 0x02b1: 0x12c1, 0x02b2: 0x12c6, 0x02b3: 0x12cd, 0x02b4: 0x12d2, + 0x02b6: 0x12d9, 0x02b7: 0x12de, 0x02b8: 0x12e5, 0x02b9: 0x12ea, 0x02ba: 0x12ef, 0x02bb: 0x04ee, + 0x02bc: 0x12f4, 0x02bd: 0x12f9, 0x02be: 0x12fd, 0x02bf: 0x12f9, // Block 0xb, offset 0x2c0 - 0x02d8: 0x04aa, 0x02d9: 0x04ae, 0x02da: 0x04b2, 0x02db: 0x04b6, 0x02dc: 0x04ba, 0x02dd: 0x04be, - 0x02e0: 0x04c2, 0x02e1: 0x04c5, 0x02e2: 0x02c8, 0x02e3: 0x04c7, - 0x02e4: 0x04c9, + 0x02c0: 0x1300, 0x02c1: 0x1309, 0x02c2: 0x130f, 0x02c3: 0x1316, 0x02c4: 0x131b, + 0x02c6: 0x1322, 0x02c7: 0x1327, 0x02c8: 0x132e, 0x02c9: 0x04f6, 0x02ca: 0x1333, 0x02cb: 0x04fb, + 0x02cc: 0x1338, 0x02cd: 0x1343, 0x02ce: 0x134f, 0x02cf: 0x135b, 0x02d0: 0x1361, 0x02d1: 0x1366, + 0x02d2: 0x136b, 0x02d3: 0x0514, 0x02d6: 0x1372, 0x02d7: 0x1377, + 0x02d8: 0x137e, 0x02d9: 0x1383, 0x02da: 0x1388, 0x02db: 0x0500, 0x02dd: 0x1393, + 0x02de: 0x139f, 0x02df: 0x13ab, 0x02e0: 0x13b1, 0x02e1: 0x13b6, 0x02e2: 0x13bb, 0x02e3: 0x0539, + 0x02e4: 0x13c2, 0x02e5: 0x13c7, 0x02e6: 0x13cc, 0x02e7: 0x13d1, 0x02e8: 0x13d8, 0x02e9: 0x13dd, + 0x02ea: 0x13e2, 0x02eb: 0x050a, 0x02ec: 0x13e7, 0x02ed: 0x13f1, 0x02ee: 0x04e8, 0x02ef: 0x13f7, + 0x02f2: 0x13f9, 0x02f3: 0x1400, 0x02f4: 0x1405, + 0x02f6: 0x140c, 0x02f7: 0x1411, 0x02f8: 0x1418, 0x02f9: 0x0505, 0x02fa: 0x141d, 0x02fb: 0x050f, + 0x02fc: 0x1422, 0x02fd: 0x0011, 0x02fe: 0x142a, // Block 0xc, offset 0x300 - 0x0300: 0x04cc, 0x0301: 0x04cf, 0x0303: 0x04d2, 0x0304: 0x04d5, - 0x0334: 0x04da, - 0x033a: 0x04dd, - 0x033e: 0x04e1, + 0x0300: 0x1486, 0x0301: 0x001c, 0x0302: 0x000d, 0x0303: 0x000f, 0x0304: 0x1488, 0x0305: 0x148a, + 0x0306: 0x148c, 0x0307: 0x148e, 0x0308: 0x1490, 0x0309: 0x1492, 0x030a: 0x1494, 0x030b: 0x1496, + 0x030c: 0x149a, 0x030d: 0x149c, 0x030e: 0x149e, 0x0310: 0x0007, 0x0311: 0x0941, + 0x0312: 0x001e, 0x0313: 0x04c7, 0x0314: 0x0943, 0x0315: 0x0494, 0x0316: 0x094e, 0x0317: 0x04c5, + 0x0318: 0x0950, 0x0319: 0x14a0, 0x031a: 0x0960, 0x031b: 0x02c8, 0x031c: 0x0962, + 0x0328: 0x14a2, // Block 0xd, offset 0x340 - 0x0344: 0x0011, 0x0345: 0x04e8, - 0x0346: 0x04ee, 0x0347: 0x04f3, 0x0348: 0x04f6, 0x0349: 0x04fb, 0x034a: 0x0500, - 0x034c: 0x0505, 0x034e: 0x050a, 0x034f: 0x050f, 0x0350: 0x0514, - 0x036a: 0x051b, 0x036b: 0x0520, 0x036c: 0x0525, 0x036d: 0x052a, 0x036e: 0x052f, 0x036f: 0x0534, - 0x0370: 0x0539, + 0x0340: 0x14a5, 0x0341: 0x14a9, 0x0342: 0x14ad, 0x0343: 0x14af, 0x0345: 0x14b3, + 0x0346: 0x14b7, 0x0347: 0x14bb, 0x0349: 0x14be, 0x034a: 0x094c, 0x034b: 0x0916, + 0x034c: 0x0916, 0x034d: 0x0916, 0x034e: 0x0494, 0x034f: 0x14c2, 0x0350: 0x0918, 0x0351: 0x0918, + 0x0352: 0x091e, 0x0353: 0x04c5, 0x0355: 0x0922, 0x0356: 0x14c5, + 0x0359: 0x0929, 0x035a: 0x14c8, 0x035b: 0x092b, 0x035c: 0x092b, 0x035d: 0x092b, + 0x0360: 0x14ca, 0x0361: 0x14cd, 0x0362: 0x14d1, + 0x0364: 0x14d4, 0x0366: 0x14d6, 0x0368: 0x14d4, + 0x036a: 0x091c, 0x036b: 0x0046, 0x036c: 0x090b, 0x036d: 0x14ad, 0x036f: 0x0941, + 0x0370: 0x090f, 0x0371: 0x14d9, 0x0373: 0x0920, 0x0374: 0x001e, 0x0375: 0x14db, + 0x0376: 0x14de, 0x0377: 0x14e1, 0x0378: 0x14e4, 0x0379: 0x097c, 0x037b: 0x14e7, + 0x037c: 0x056f, 0x037d: 0x0973, 0x037e: 0x14eb, 0x037f: 0x14ee, // Block 0xe, offset 0x380 - 0x038a: 0x0540, 0x038b: 0x0545, - 0x038c: 0x054a, 0x038d: 0x054f, 0x038e: 0x0554, 0x0390: 0x0559, 0x0391: 0x055c, - 0x0392: 0x055f, 0x0393: 0x050a, 0x0394: 0x0520, 0x0395: 0x056c, 0x0396: 0x056f, - 0x03b0: 0x0572, 0x03b1: 0x0575, 0x03b2: 0x0578, 0x03b4: 0x057b, 0x03b5: 0x057e, - 0x03b9: 0x0581, + 0x0380: 0x14f1, 0x0385: 0x090d, + 0x0386: 0x093f, 0x0387: 0x0941, 0x0388: 0x097c, 0x0389: 0x0499, + 0x0390: 0x14f5, 0x0391: 0x14fb, + 0x0392: 0x1501, 0x0393: 0x1508, 0x0394: 0x150e, 0x0395: 0x1514, 0x0396: 0x151a, 0x0397: 0x1520, + 0x0398: 0x1526, 0x0399: 0x152c, 0x039a: 0x1532, 0x039b: 0x1538, 0x039c: 0x153e, 0x039d: 0x1544, + 0x039e: 0x154a, 0x039f: 0x1550, 0x03a0: 0x0918, 0x03a1: 0x1555, 0x03a2: 0x1558, 0x03a3: 0x155c, + 0x03a4: 0x155f, 0x03a5: 0x1561, 0x03a6: 0x1564, 0x03a7: 0x1568, 0x03a8: 0x156d, 0x03a9: 0x1570, + 0x03aa: 0x1572, 0x03ab: 0x1575, 0x03ac: 0x091e, 0x03ad: 0x14ad, 0x03ae: 0x090d, 0x03af: 0x0920, + 0x03b0: 0x097c, 0x03b1: 0x1579, 0x03b2: 0x157c, 0x03b3: 0x1580, 0x03b4: 0x096d, 0x03b5: 0x1583, + 0x03b6: 0x1586, 0x03b7: 0x158a, 0x03b8: 0x158f, 0x03b9: 0x04c7, 0x03ba: 0x1592, 0x03bb: 0x1595, + 0x03bc: 0x04c5, 0x03bd: 0x0984, 0x03be: 0x093f, 0x03bf: 0x0950, // Block 0xf, offset 0x3c0 - 0x03c0: 0x0584, 0x03c1: 0x0589, 0x03c3: 0x058e, - 0x03c7: 0x0593, - 0x03cc: 0x0598, 0x03cd: 0x059d, 0x03ce: 0x05a2, - 0x03d9: 0x05a7, - 0x03f9: 0x05ac, + 0x03e0: 0x001c, 0x03e1: 0x000d, 0x03e2: 0x000f, 0x03e3: 0x1488, + 0x03e4: 0x148a, 0x03e5: 0x148c, 0x03e6: 0x148e, 0x03e7: 0x1490, 0x03e8: 0x1492, 0x03e9: 0x16cb, + 0x03ea: 0x16ce, 0x03eb: 0x16d1, 0x03ec: 0x16d4, 0x03ed: 0x16d7, 0x03ee: 0x16da, 0x03ef: 0x16dd, + 0x03f0: 0x16e0, 0x03f1: 0x16e3, 0x03f2: 0x16e6, 0x03f3: 0x16e9, 0x03f4: 0x16ec, 0x03f5: 0x16f0, + 0x03f6: 0x16f4, 0x03f7: 0x16f8, 0x03f8: 0x16fc, 0x03f9: 0x1700, 0x03fa: 0x1704, 0x03fb: 0x1708, + 0x03fc: 0x170c, 0x03fd: 0x1710, 0x03fe: 0x1715, 0x03ff: 0x171a, // Block 0x10, offset 0x400 - 0x0410: 0x05b1, 0x0411: 0x05b6, - 0x0413: 0x05bb, 0x0417: 0x05c0, - 0x041c: 0x05c5, 0x041d: 0x05ca, - 0x041e: 0x05cf, - 0x0436: 0x05d4, 0x0437: 0x05d9, + 0x0400: 0x171f, 0x0401: 0x1724, 0x0402: 0x1729, 0x0403: 0x172e, 0x0404: 0x1733, 0x0405: 0x1738, + 0x0406: 0x173d, 0x0407: 0x1742, 0x0408: 0x1747, 0x0409: 0x174a, 0x040a: 0x174d, 0x040b: 0x1750, + 0x040c: 0x1753, 0x040d: 0x1756, 0x040e: 0x1759, 0x040f: 0x175c, 0x0410: 0x175f, 0x0411: 0x1762, + 0x0412: 0x1766, 0x0413: 0x176a, 0x0414: 0x176e, 0x0415: 0x1772, 0x0416: 0x1776, 0x0417: 0x177a, + 0x0418: 0x177e, 0x0419: 0x1782, 0x041a: 0x1786, 0x041b: 0x178a, 0x041c: 0x178e, 0x041d: 0x1792, + 0x041e: 0x1796, 0x041f: 0x179a, 0x0420: 0x179e, 0x0421: 0x17a2, 0x0422: 0x17a6, 0x0423: 0x17aa, + 0x0424: 0x17ae, 0x0425: 0x17b2, 0x0426: 0x17b6, 0x0427: 0x17ba, 0x0428: 0x17be, 0x0429: 0x17c2, + 0x042a: 0x17c6, 0x042b: 0x17ca, 0x042c: 0x17ce, 0x042d: 0x17d2, 0x042e: 0x17d6, 0x042f: 0x17da, + 0x0430: 0x17de, 0x0431: 0x17e2, 0x0432: 0x17e6, 0x0433: 0x17ea, 0x0434: 0x17ee, 0x0435: 0x17f2, + 0x0436: 0x0906, 0x0437: 0x090b, 0x0438: 0x14ad, 0x0439: 0x090d, 0x043a: 0x090f, 0x043b: 0x14d9, + 0x043c: 0x0914, 0x043d: 0x0916, 0x043e: 0x0918, 0x043f: 0x091a, // Block 0x11, offset 0x440 - 0x0441: 0x05de, 0x0442: 0x05e3, - 0x0450: 0x05e8, 0x0451: 0x05ed, - 0x0452: 0x05f2, 0x0453: 0x05f7, 0x0456: 0x05fc, 0x0457: 0x0601, - 0x045a: 0x0606, 0x045b: 0x060b, 0x045c: 0x0610, 0x045d: 0x0615, - 0x045e: 0x061a, 0x045f: 0x061f, 0x0462: 0x0624, 0x0463: 0x0629, - 0x0464: 0x062e, 0x0465: 0x0633, 0x0466: 0x0638, 0x0467: 0x063d, - 0x046a: 0x0642, 0x046b: 0x0647, 0x046c: 0x064c, 0x046d: 0x0651, 0x046e: 0x0656, 0x046f: 0x065b, - 0x0470: 0x0660, 0x0471: 0x0665, 0x0472: 0x066a, 0x0473: 0x066f, 0x0474: 0x0674, 0x0475: 0x0679, - 0x0478: 0x067e, 0x0479: 0x0683, + 0x0440: 0x091c, 0x0441: 0x091e, 0x0442: 0x0920, 0x0443: 0x0922, 0x0444: 0x0924, 0x0445: 0x0929, + 0x0446: 0x14c8, 0x0447: 0x092b, 0x0448: 0x17f6, 0x0449: 0x092d, 0x044a: 0x092f, 0x044b: 0x155f, + 0x044c: 0x0931, 0x044d: 0x1570, 0x044e: 0x17f8, 0x044f: 0x14d4, 0x0450: 0x0007, 0x0451: 0x093d, + 0x0452: 0x0984, 0x0453: 0x093f, 0x0454: 0x0941, 0x0455: 0x098c, 0x0456: 0x094c, 0x0457: 0x0494, + 0x0458: 0x097c, 0x0459: 0x0499, 0x045a: 0x094e, 0x045b: 0x04c5, 0x045c: 0x0950, 0x045d: 0x14a0, + 0x045e: 0x001e, 0x045f: 0x0960, 0x0460: 0x17fa, 0x0461: 0x049b, 0x0462: 0x02c8, 0x0463: 0x0962, + 0x0464: 0x0964, 0x0465: 0x096d, 0x0466: 0x04a6, 0x0467: 0x04c7, 0x0468: 0x04a8, 0x0469: 0x09df, + 0x046a: 0x1486, // Block 0x12, offset 0x480 - 0x0487: 0x0688, + 0x048c: 0x1b8a, 0x048e: 0x1b91, 0x0490: 0x1b98, + 0x0492: 0x1b9f, 0x0494: 0x1ba6, 0x0496: 0x1bad, + 0x0498: 0x1bb4, 0x049a: 0x1bbb, 0x049c: 0x1bc2, + 0x049e: 0x1bc9, 0x04a0: 0x1bd0, 0x04a2: 0x1bd7, + 0x04a5: 0x1bde, 0x04a7: 0x1be5, 0x04a9: 0x1bec, + 0x04b0: 0x1bf3, 0x04b1: 0x1bfa, 0x04b3: 0x1c01, 0x04b4: 0x1c08, + 0x04b6: 0x1c0f, 0x04b7: 0x1c16, 0x04b9: 0x1c1d, 0x04ba: 0x1c24, + 0x04bc: 0x1c2b, 0x04bd: 0x1c32, // Block 0x13, offset 0x4c0 - 0x04e2: 0x068d, 0x04e3: 0x0692, - 0x04e4: 0x0697, 0x04e5: 0x069c, 0x04e6: 0x06a1, + 0x04c0: 0x1ed8, 0x04c1: 0x1ede, 0x04c2: 0x1ee4, 0x04c3: 0x1eea, 0x04c4: 0x1ef0, 0x04c5: 0x1ef6, + 0x04c6: 0x1efc, 0x04c7: 0x1f02, 0x04c8: 0x1f08, 0x04c9: 0x1f0e, 0x04ca: 0x1f14, 0x04cb: 0x1f1a, + 0x04cc: 0x1f20, 0x04cd: 0x1f26, 0x04ce: 0x1f2c, 0x04cf: 0x1f35, 0x04d0: 0x1f3e, 0x04d1: 0x1f47, + 0x04d2: 0x1f50, 0x04d3: 0x1f59, 0x04d4: 0x1f62, 0x04d5: 0x1f6b, 0x04d6: 0x1f74, 0x04d7: 0x1f7d, + 0x04d8: 0x1f86, 0x04d9: 0x1f8f, 0x04da: 0x1f98, 0x04db: 0x1fa1, 0x04dc: 0x1faa, 0x04dd: 0x1fb3, + 0x04de: 0x1fc5, 0x04e0: 0x1fd4, 0x04e1: 0x1fda, 0x04e2: 0x1fe0, 0x04e3: 0x1fe6, + 0x04e4: 0x1fec, 0x04e5: 0x1ff2, 0x04e6: 0x1ff8, 0x04e7: 0x1ffe, 0x04e8: 0x2004, 0x04e9: 0x200a, + 0x04ea: 0x2010, 0x04eb: 0x2016, 0x04ec: 0x201c, 0x04ed: 0x2022, 0x04ee: 0x2028, 0x04ef: 0x202e, + 0x04f0: 0x2034, 0x04f1: 0x203a, 0x04f2: 0x2040, 0x04f3: 0x2046, 0x04f4: 0x204c, 0x04f5: 0x2052, + 0x04f6: 0x2058, 0x04f7: 0x205e, 0x04f8: 0x2064, 0x04f9: 0x206a, 0x04fa: 0x2070, 0x04fb: 0x2076, + 0x04fc: 0x207c, 0x04fd: 0x2082, 0x04fe: 0x2088, 0x04ff: 0x208e, // Block 0x14, offset 0x500 - 0x0535: 0x06a6, - 0x0536: 0x06ab, 0x0537: 0x06b0, 0x0538: 0x06b5, + 0x0500: 0x2094, 0x0501: 0x209a, 0x0502: 0x20a0, 0x0503: 0x20a6, 0x0504: 0x20ac, 0x0505: 0x20b0, + 0x0506: 0x192e, 0x0507: 0x20b4, + 0x0510: 0x20b8, 0x0511: 0x20bc, + 0x0512: 0x20bf, 0x0513: 0x20c2, 0x0514: 0x20c5, 0x0515: 0x20c8, 0x0516: 0x20cb, 0x0517: 0x20ce, + 0x0518: 0x20d1, 0x0519: 0x20d4, 0x051a: 0x20d7, 0x051b: 0x20da, 0x051c: 0x20dd, 0x051d: 0x20e0, + 0x051e: 0x20e3, 0x051f: 0x20e6, 0x0520: 0x1d38, 0x0521: 0x1d44, 0x0522: 0x1d50, 0x0523: 0x1d58, + 0x0524: 0x1d78, 0x0525: 0x1d7c, 0x0526: 0x1d88, 0x0527: 0x1d90, 0x0528: 0x1d94, 0x0529: 0x1d9c, + 0x052a: 0x1da0, 0x052b: 0x1da4, 0x052c: 0x1da8, 0x052d: 0x1dac, 0x052e: 0x20e9, 0x052f: 0x20f0, + 0x0530: 0x20f7, 0x0531: 0x20fe, 0x0532: 0x2105, 0x0533: 0x210c, 0x0534: 0x2113, 0x0535: 0x211a, + 0x0536: 0x2121, 0x0537: 0x2128, 0x0538: 0x212f, 0x0539: 0x2136, 0x053a: 0x213d, 0x053b: 0x2144, + 0x053c: 0x214b, 0x053d: 0x215b, 0x053e: 0x2168, // Block 0x15, offset 0x540 - 0x0540: 0x06ba, 0x0542: 0x06bf, - 0x0553: 0x06c4, + 0x0540: 0x1826, 0x0541: 0x183e, 0x0542: 0x1eb0, 0x0543: 0x1eb4, 0x0544: 0x216f, 0x0545: 0x2173, + 0x0546: 0x2177, 0x0547: 0x1852, 0x0548: 0x217b, 0x0549: 0x1882, 0x054a: 0x194a, 0x054b: 0x197a, + 0x054c: 0x1976, 0x054d: 0x194e, 0x054e: 0x1abe, 0x054f: 0x18a2, 0x0550: 0x1942, 0x0551: 0x217f, + 0x0552: 0x2183, 0x0553: 0x2187, 0x0554: 0x218b, 0x0555: 0x218f, 0x0556: 0x2193, 0x0557: 0x2197, + 0x0558: 0x219b, 0x0559: 0x219f, 0x055a: 0x21a3, 0x055b: 0x18ba, 0x055c: 0x21a7, 0x055d: 0x21ab, + 0x055e: 0x21af, 0x055f: 0x21b3, 0x0560: 0x21b7, 0x0561: 0x21bb, 0x0562: 0x21bf, 0x0563: 0x21c3, + 0x0564: 0x1eb8, 0x0565: 0x1ebc, 0x0566: 0x1ec0, 0x0567: 0x21c7, 0x0568: 0x21cb, 0x0569: 0x21cf, + 0x056a: 0x21d3, 0x056b: 0x21d7, 0x056c: 0x21db, 0x056d: 0x21df, 0x056e: 0x21e3, 0x056f: 0x21e7, + 0x0570: 0x21eb, 0x0571: 0x21ef, 0x0572: 0x21f2, 0x0573: 0x21f5, 0x0574: 0x21f8, 0x0575: 0x21fb, + 0x0576: 0x21fe, 0x0577: 0x2201, 0x0578: 0x2204, 0x0579: 0x2207, 0x057a: 0x220a, 0x057b: 0x220d, + 0x057c: 0x2210, 0x057d: 0x2213, 0x057e: 0x2216, 0x057f: 0x2219, // Block 0x16, offset 0x580 - 0x05a9: 0x06c9, - 0x05b1: 0x06d0, 0x05b4: 0x06d7, + 0x0580: 0x2325, 0x0581: 0x2335, 0x0582: 0x2342, 0x0583: 0x2352, 0x0584: 0x235c, 0x0585: 0x236c, + 0x0586: 0x2376, 0x0587: 0x2380, 0x0588: 0x2393, 0x0589: 0x23a0, 0x058a: 0x23aa, 0x058b: 0x23b4, + 0x058c: 0x23be, 0x058d: 0x23cb, 0x058e: 0x23d8, 0x058f: 0x23e5, 0x0590: 0x23f2, 0x0591: 0x23ff, + 0x0592: 0x240c, 0x0593: 0x2419, 0x0594: 0x242c, 0x0595: 0x2433, 0x0596: 0x2446, 0x0597: 0x2459, + 0x0598: 0x2469, 0x0599: 0x2476, 0x059a: 0x2489, 0x059b: 0x249c, 0x059c: 0x24a9, 0x059d: 0x24b3, + 0x059e: 0x24bd, 0x059f: 0x24ca, 0x05a0: 0x24d7, 0x05a1: 0x24e7, 0x05a2: 0x24f7, 0x05a3: 0x2501, + 0x05a4: 0x250b, 0x05a5: 0x2518, 0x05a6: 0x2522, 0x05a7: 0x252c, 0x05a8: 0x2533, 0x05a9: 0x253a, + 0x05aa: 0x2544, 0x05ab: 0x254e, 0x05ac: 0x2561, 0x05ad: 0x256e, 0x05ae: 0x257e, 0x05af: 0x2591, + 0x05b0: 0x259e, 0x05b1: 0x25a8, 0x05b2: 0x25b2, 0x05b3: 0x25c5, 0x05b4: 0x25d2, 0x05b5: 0x25e5, + 0x05b6: 0x25ef, 0x05b7: 0x25ff, 0x05b8: 0x2609, 0x05b9: 0x2616, 0x05ba: 0x2620, 0x05bb: 0x262d, + 0x05bc: 0x263d, 0x05bd: 0x264a, 0x05be: 0x265a, 0x05bf: 0x2667, // Block 0x17, offset 0x5c0 - 0x05d8: 0x06de, 0x05d9: 0x06e5, 0x05da: 0x06ec, 0x05db: 0x06f3, 0x05dc: 0x06fa, 0x05dd: 0x0701, - 0x05de: 0x0708, 0x05df: 0x070f, + 0x05c0: 0x266e, 0x05c1: 0x267e, 0x05c2: 0x2688, 0x05c3: 0x2692, 0x05c4: 0x269f, 0x05c5: 0x26a9, + 0x05c6: 0x26b3, 0x05c7: 0x26bd, 0x05c8: 0x26cd, 0x05c9: 0x26da, 0x05ca: 0x26e1, 0x05cb: 0x26f4, + 0x05cc: 0x26fe, 0x05cd: 0x270e, 0x05ce: 0x271b, 0x05cf: 0x2728, 0x05d0: 0x2732, 0x05d1: 0x273c, + 0x05d2: 0x2749, 0x05d3: 0x2750, 0x05d4: 0x275d, 0x05d5: 0x276d, 0x05d6: 0x2774, 0x05d7: 0x2787, + 0x05d8: 0x2791, 0x05d9: 0x2796, 0x05da: 0x279b, 0x05db: 0x27a0, 0x05dc: 0x27a5, 0x05dd: 0x27aa, + 0x05de: 0x27af, 0x05df: 0x27b4, 0x05e0: 0x27b9, 0x05e1: 0x27be, 0x05e2: 0x27c3, 0x05e3: 0x27c9, + 0x05e4: 0x27cf, 0x05e5: 0x27d5, 0x05e6: 0x27db, 0x05e7: 0x27e1, 0x05e8: 0x27e7, 0x05e9: 0x27ed, + 0x05ea: 0x27f3, 0x05eb: 0x27f9, 0x05ec: 0x27ff, 0x05ed: 0x2805, 0x05ee: 0x280b, 0x05ef: 0x2811, + 0x05f0: 0x2817, 0x05f1: 0x281d, 0x05f2: 0x2821, 0x05f3: 0x2824, 0x05f4: 0x2827, 0x05f5: 0x282b, + 0x05f6: 0x282e, 0x05f7: 0x2831, 0x05f8: 0x2834, 0x05f9: 0x2838, 0x05fa: 0x283c, 0x05fb: 0x283f, + 0x05fc: 0x2846, 0x05fd: 0x284d, 0x05fe: 0x2854, 0x05ff: 0x285b, // Block 0x18, offset 0x600 - 0x060b: 0x0716, - 0x060c: 0x071d, - 0x061c: 0x0724, 0x061d: 0x072b, - 0x061f: 0x0732, + 0x0600: 0x2868, 0x0601: 0x286b, 0x0602: 0x286e, 0x0603: 0x2872, 0x0604: 0x2875, 0x0605: 0x2878, + 0x0606: 0x287b, 0x0607: 0x287e, 0x0608: 0x2881, 0x0609: 0x2885, 0x060a: 0x288a, 0x060b: 0x288d, + 0x060c: 0x2890, 0x060d: 0x2894, 0x060e: 0x2898, 0x060f: 0x289b, 0x0610: 0x289e, 0x0611: 0x28a1, + 0x0612: 0x28a5, 0x0613: 0x28a9, 0x0614: 0x28ad, 0x0615: 0x28b1, 0x0616: 0x28b5, 0x0617: 0x28b8, + 0x0618: 0x28bb, 0x0619: 0x28be, 0x061a: 0x28c1, 0x061b: 0x28c4, 0x061c: 0x28c8, 0x061d: 0x28cb, + 0x061e: 0x28ce, 0x061f: 0x28d1, 0x0620: 0x28d5, 0x0621: 0x28d9, 0x0622: 0x28dc, 0x0623: 0x28e0, + 0x0624: 0x28e4, 0x0625: 0x28e8, 0x0626: 0x28eb, 0x0627: 0x28ef, 0x0628: 0x28f5, 0x0629: 0x28fc, + 0x062a: 0x28ff, 0x062b: 0x2903, 0x062c: 0x2907, 0x062d: 0x290b, 0x062e: 0x290f, 0x062f: 0x2917, + 0x0630: 0x2920, 0x0631: 0x2923, 0x0632: 0x2926, 0x0633: 0x292a, 0x0634: 0x292d, 0x0635: 0x2930, + 0x0636: 0x2933, 0x0637: 0x2937, 0x0638: 0x293a, 0x0639: 0x293d, 0x063a: 0x2940, 0x063b: 0x2943, + 0x063c: 0x2946, 0x063d: 0x294a, 0x063e: 0x294d, 0x063f: 0x2950, // Block 0x19, offset 0x640 - 0x0673: 0x0739, - 0x0676: 0x0740, + 0x0640: 0x2953, 0x0641: 0x2957, 0x0642: 0x295b, 0x0643: 0x2960, 0x0644: 0x2963, 0x0645: 0x2966, + 0x0646: 0x2969, 0x0647: 0x2970, 0x0648: 0x2974, 0x0649: 0x2977, 0x064a: 0x297a, 0x064b: 0x297d, + 0x064c: 0x2980, 0x064d: 0x2983, 0x064e: 0x2986, 0x064f: 0x2989, 0x0650: 0x298c, 0x0651: 0x298f, + 0x0652: 0x2992, 0x0653: 0x2996, 0x0654: 0x2999, 0x0655: 0x299c, 0x0656: 0x29a0, 0x0657: 0x29a4, + 0x0658: 0x29a7, 0x0659: 0x29ac, 0x065a: 0x29b0, 0x065b: 0x29b3, 0x065c: 0x29b6, 0x065d: 0x29b9, + 0x065e: 0x29bc, 0x065f: 0x29c2, 0x0660: 0x29c8, 0x0661: 0x29cd, 0x0662: 0x29d2, 0x0663: 0x29d7, + 0x0664: 0x29dc, 0x0665: 0x29e1, 0x0666: 0x29e6, 0x0667: 0x29eb, 0x0668: 0x29f0, 0x0669: 0x29f5, + 0x066a: 0x29fb, 0x066b: 0x2a01, 0x066c: 0x2a07, 0x066d: 0x2a0d, 0x066e: 0x2a13, 0x066f: 0x2a19, + 0x0670: 0x2a1f, 0x0671: 0x2a25, 0x0672: 0x2a2b, 0x0673: 0x2a31, 0x0674: 0x2a37, 0x0675: 0x2a3d, + 0x0676: 0x2a43, 0x0677: 0x2a49, 0x0678: 0x2a4f, 0x0679: 0x2a55, 0x067a: 0x2a5b, 0x067b: 0x2a61, + 0x067c: 0x2a67, 0x067d: 0x2a6d, 0x067e: 0x2a73, 0x067f: 0x2a79, // Block 0x1a, offset 0x680 - 0x0699: 0x0747, 0x069a: 0x074e, 0x069b: 0x0755, - 0x069e: 0x075c, + 0x0680: 0x2fce, 0x0681: 0x2fd2, 0x0682: 0x2fd6, 0x0683: 0x2fda, 0x0684: 0x2fde, 0x0685: 0x2fe2, + 0x0686: 0x2fe6, 0x0687: 0x2fea, 0x0688: 0x2fee, 0x0689: 0x2eed, 0x068a: 0x2ff2, 0x068b: 0x2ef1, + 0x068c: 0x2ff6, 0x068d: 0x2ffa, 0x068e: 0x2ffe, 0x068f: 0x3002, 0x0690: 0x3006, 0x0691: 0x2e6d, + 0x0692: 0x2b15, 0x0693: 0x300a, 0x0694: 0x300e, 0x0695: 0x195a, 0x0696: 0x2c25, 0x0697: 0x2d71, + 0x0698: 0x3012, 0x0699: 0x3016, 0x069a: 0x2f0d, 0x069b: 0x301a, 0x069c: 0x2f11, 0x069d: 0x301e, + 0x069e: 0x3022, 0x069f: 0x3026, 0x06a0: 0x2e75, 0x06a1: 0x302a, 0x06a2: 0x302e, 0x06a3: 0x3032, + 0x06a4: 0x3036, 0x06a5: 0x303a, 0x06a6: 0x2e79, 0x06a7: 0x303e, 0x06a8: 0x3042, 0x06a9: 0x3046, + 0x06aa: 0x304a, 0x06ab: 0x304e, 0x06ac: 0x3052, 0x06ad: 0x2f41, 0x06ae: 0x3056, 0x06af: 0x305a, + 0x06b0: 0x2cb1, 0x06b1: 0x305e, 0x06b2: 0x2f51, 0x06b3: 0x3062, 0x06b4: 0x3066, 0x06b5: 0x306a, + 0x06b6: 0x306e, 0x06b7: 0x3072, 0x06b8: 0x2f65, 0x06b9: 0x3076, 0x06ba: 0x2e99, 0x06bb: 0x307a, + 0x06bc: 0x2f69, 0x06bd: 0x2bd9, 0x06be: 0x307e, 0x06bf: 0x2f6d, // Block 0x1b, offset 0x6c0 - 0x06c8: 0x0763, 0x06cb: 0x076a, - 0x06cc: 0x0771, - 0x06dc: 0x0778, 0x06dd: 0x077f, + 0x06c0: 0x3082, 0x06c1: 0x2f75, 0x06c2: 0x3086, 0x06c3: 0x308a, 0x06c4: 0x308e, 0x06c5: 0x3092, + 0x06c6: 0x3096, 0x06c7: 0x2f7d, 0x06c8: 0x2e8d, 0x06c9: 0x309a, 0x06ca: 0x2f81, 0x06cb: 0x309e, + 0x06cc: 0x2f85, 0x06cd: 0x30a2, 0x06ce: 0x1b76, 0x06cf: 0x30a6, 0x06d0: 0x30ab, 0x06d1: 0x30b0, + 0x06d2: 0x30b5, 0x06d3: 0x30b9, 0x06d4: 0x30bd, 0x06d5: 0x30c1, 0x06d6: 0x30c6, 0x06d7: 0x30cb, + 0x06d8: 0x30d0, 0x06d9: 0x30d4, // Block 0x1c, offset 0x700 - 0x0714: 0x0786, + 0x0700: 0x30d8, 0x0701: 0x30db, 0x0702: 0x30de, 0x0703: 0x30e1, 0x0704: 0x30e5, 0x0705: 0x30e9, + 0x0706: 0x30e9, + 0x0713: 0x30ec, 0x0714: 0x30f1, 0x0715: 0x30f6, 0x0716: 0x30fb, 0x0717: 0x3100, + 0x071d: 0x3105, + 0x071f: 0x310a, 0x0720: 0x310f, 0x0721: 0x14db, 0x0722: 0x14e4, 0x0723: 0x3112, + 0x0724: 0x3115, 0x0725: 0x3118, 0x0726: 0x311b, 0x0727: 0x311e, 0x0728: 0x3121, 0x0729: 0x1494, + 0x072a: 0x3124, 0x072b: 0x3129, 0x072c: 0x312e, 0x072d: 0x3135, 0x072e: 0x313c, 0x072f: 0x3141, + 0x0730: 0x3146, 0x0731: 0x314b, 0x0732: 0x3150, 0x0733: 0x3155, 0x0734: 0x315a, 0x0735: 0x315f, + 0x0736: 0x3164, 0x0738: 0x3169, 0x0739: 0x316e, 0x073a: 0x3173, 0x073b: 0x3178, + 0x073c: 0x317d, 0x073e: 0x3182, // Block 0x1d, offset 0x740 - 0x074a: 0x078d, 0x074b: 0x0794, - 0x074c: 0x079b, + 0x0740: 0x3187, 0x0741: 0x318c, 0x0743: 0x3191, 0x0744: 0x3196, + 0x0746: 0x319b, 0x0747: 0x31a0, 0x0748: 0x31a5, 0x0749: 0x31aa, 0x074a: 0x31af, 0x074b: 0x31b4, + 0x074c: 0x31b9, 0x074d: 0x31be, 0x074e: 0x31c3, 0x074f: 0x31c8, 0x0750: 0x31cd, 0x0751: 0x31cd, + 0x0752: 0x31d0, 0x0753: 0x31d0, 0x0754: 0x31d0, 0x0755: 0x31d0, 0x0756: 0x31d3, 0x0757: 0x31d3, + 0x0758: 0x31d3, 0x0759: 0x31d3, 0x075a: 0x31d6, 0x075b: 0x31d6, 0x075c: 0x31d6, 0x075d: 0x31d6, + 0x075e: 0x31d9, 0x075f: 0x31d9, 0x0760: 0x31d9, 0x0761: 0x31d9, 0x0762: 0x31dc, 0x0763: 0x31dc, + 0x0764: 0x31dc, 0x0765: 0x31dc, 0x0766: 0x31df, 0x0767: 0x31df, 0x0768: 0x31df, 0x0769: 0x31df, + 0x076a: 0x31e2, 0x076b: 0x31e2, 0x076c: 0x31e2, 0x076d: 0x31e2, 0x076e: 0x31e5, 0x076f: 0x31e5, + 0x0770: 0x31e5, 0x0771: 0x31e5, 0x0772: 0x31e8, 0x0773: 0x31e8, 0x0774: 0x31e8, 0x0775: 0x31e8, + 0x0776: 0x31eb, 0x0777: 0x31eb, 0x0778: 0x31eb, 0x0779: 0x31eb, 0x077a: 0x31ee, 0x077b: 0x31ee, + 0x077c: 0x31ee, 0x077d: 0x31ee, 0x077e: 0x31f1, 0x077f: 0x31f1, // Block 0x1e, offset 0x780 - 0x0788: 0x07a2, + 0x0780: 0x31f1, 0x0781: 0x31f1, 0x0782: 0x31f4, 0x0783: 0x31f4, 0x0784: 0x31f7, 0x0785: 0x31f7, + 0x0786: 0x31fa, 0x0787: 0x31fa, 0x0788: 0x31fd, 0x0789: 0x31fd, 0x078a: 0x3200, 0x078b: 0x3200, + 0x078c: 0x3203, 0x078d: 0x3203, 0x078e: 0x3206, 0x078f: 0x3206, 0x0790: 0x3206, 0x0791: 0x3206, + 0x0792: 0x3209, 0x0793: 0x3209, 0x0794: 0x3209, 0x0795: 0x3209, 0x0796: 0x320c, 0x0797: 0x320c, + 0x0798: 0x320c, 0x0799: 0x320c, 0x079a: 0x320f, 0x079b: 0x320f, 0x079c: 0x320f, 0x079d: 0x320f, + 0x079e: 0x3212, 0x079f: 0x3212, 0x07a0: 0x3215, 0x07a1: 0x3215, 0x07a2: 0x3215, 0x07a3: 0x3215, + 0x07a4: 0x06ba, 0x07a5: 0x06ba, 0x07a6: 0x3218, 0x07a7: 0x3218, 0x07a8: 0x3218, 0x07a9: 0x3218, + 0x07aa: 0x321b, 0x07ab: 0x321b, 0x07ac: 0x321b, 0x07ad: 0x321b, 0x07ae: 0x321e, 0x07af: 0x321e, + 0x07b0: 0x06c4, 0x07b1: 0x06c4, // Block 0x1f, offset 0x7c0 - 0x07c0: 0x07a9, - 0x07c7: 0x07b0, 0x07c8: 0x07b7, 0x07ca: 0x07be, 0x07cb: 0x07c5, + 0x07d3: 0x3221, 0x07d4: 0x3221, 0x07d5: 0x3221, 0x07d6: 0x3221, 0x07d7: 0x3224, + 0x07d8: 0x3224, 0x07d9: 0x3227, 0x07da: 0x3227, 0x07db: 0x322a, 0x07dc: 0x322a, 0x07dd: 0x06b0, + 0x07de: 0x322d, 0x07df: 0x322d, 0x07e0: 0x3230, 0x07e1: 0x3230, 0x07e2: 0x3233, 0x07e3: 0x3233, + 0x07e4: 0x3236, 0x07e5: 0x3236, 0x07e6: 0x3236, 0x07e7: 0x3236, 0x07e8: 0x3239, 0x07e9: 0x3239, + 0x07ea: 0x323c, 0x07eb: 0x323c, 0x07ec: 0x3243, 0x07ed: 0x3243, 0x07ee: 0x324a, 0x07ef: 0x324a, + 0x07f0: 0x3251, 0x07f1: 0x3251, 0x07f2: 0x3258, 0x07f3: 0x3258, 0x07f4: 0x325f, 0x07f5: 0x325f, + 0x07f6: 0x3266, 0x07f7: 0x3266, 0x07f8: 0x3266, 0x07f9: 0x326d, 0x07fa: 0x326d, 0x07fb: 0x326d, + 0x07fc: 0x3274, 0x07fd: 0x3274, 0x07fe: 0x3274, 0x07ff: 0x3274, // Block 0x20, offset 0x800 - 0x080a: 0x07cf, 0x080b: 0x07d6, - 0x080c: 0x07dd, + 0x0800: 0x33ba, 0x0801: 0x33bf, 0x0802: 0x33c4, 0x0803: 0x33c9, 0x0804: 0x33ce, 0x0805: 0x33d3, + 0x0806: 0x33d8, 0x0807: 0x33dd, 0x0808: 0x33e2, 0x0809: 0x33e7, 0x080a: 0x33ec, 0x080b: 0x33f1, + 0x080c: 0x33f6, 0x080d: 0x33fb, 0x080e: 0x3400, 0x080f: 0x3405, 0x0810: 0x340a, 0x0811: 0x340f, + 0x0812: 0x3414, 0x0813: 0x3419, 0x0814: 0x341e, 0x0815: 0x3423, 0x0816: 0x3428, 0x0817: 0x342d, + 0x0818: 0x3432, 0x0819: 0x3437, 0x081a: 0x343c, 0x081b: 0x3441, 0x081c: 0x3446, 0x081d: 0x344b, + 0x081e: 0x3450, 0x081f: 0x3456, 0x0820: 0x345c, 0x0821: 0x3462, 0x0822: 0x3468, 0x0823: 0x346e, + 0x0824: 0x3474, 0x0825: 0x347b, 0x0826: 0x3285, 0x0827: 0x3482, 0x0828: 0x326d, 0x0829: 0x328c, + 0x082a: 0x3489, 0x082b: 0x348e, 0x082c: 0x32a2, 0x082d: 0x3493, 0x082e: 0x32a7, 0x082f: 0x32ac, + 0x0830: 0x3498, 0x0831: 0x349d, 0x0832: 0x32c0, 0x0833: 0x34a2, 0x0834: 0x32c5, 0x0835: 0x32ca, + 0x0836: 0x34a7, 0x0837: 0x34ac, 0x0838: 0x32d4, 0x0839: 0x34b1, 0x083a: 0x32d9, 0x083b: 0x32de, + 0x083c: 0x336f, 0x083d: 0x3374, 0x083e: 0x3383, 0x083f: 0x3388, // Block 0x21, offset 0x840 - 0x085a: 0x07e4, 0x085c: 0x07eb, 0x085d: 0x07f2, - 0x085e: 0x07fc, + 0x0840: 0x338d, 0x0841: 0x33a1, 0x0842: 0x33a6, 0x0843: 0x33ab, 0x0844: 0x33b0, 0x0845: 0x33c4, + 0x0846: 0x33c9, 0x0847: 0x33ce, 0x0848: 0x34b6, 0x0849: 0x33e2, 0x084a: 0x34bb, 0x084b: 0x34c0, + 0x084c: 0x3400, 0x084d: 0x34c5, 0x084e: 0x3405, 0x084f: 0x340a, 0x0850: 0x344b, 0x0851: 0x34ca, + 0x0852: 0x34cf, 0x0853: 0x3432, 0x0854: 0x34d4, 0x0855: 0x3437, 0x0856: 0x343c, 0x0857: 0x3277, + 0x0858: 0x327e, 0x0859: 0x34d9, 0x085a: 0x3285, 0x085b: 0x34e0, 0x085c: 0x3293, 0x085d: 0x3298, + 0x085e: 0x329d, 0x085f: 0x32a2, 0x0860: 0x34e7, 0x0861: 0x32b1, 0x0862: 0x32b6, 0x0863: 0x32bb, + 0x0864: 0x32c0, 0x0865: 0x34ec, 0x0866: 0x32d4, 0x0867: 0x32e3, 0x0868: 0x32e8, 0x0869: 0x32ed, + 0x086a: 0x32f2, 0x086b: 0x32f7, 0x086c: 0x3301, 0x086d: 0x3306, 0x086e: 0x330b, 0x086f: 0x3310, + 0x0870: 0x3315, 0x0871: 0x331a, 0x0872: 0x34f1, 0x0873: 0x331f, 0x0874: 0x3324, 0x0875: 0x3329, + 0x0876: 0x332e, 0x0877: 0x3333, 0x0878: 0x3338, 0x0879: 0x3342, 0x087a: 0x3347, 0x087b: 0x334c, + 0x087c: 0x3351, 0x087d: 0x3356, 0x087e: 0x335b, 0x087f: 0x3360, // Block 0x22, offset 0x880 - 0x08b3: 0x0803, + 0x0880: 0x3365, 0x0881: 0x336a, 0x0882: 0x3379, 0x0883: 0x337e, 0x0884: 0x3392, 0x0885: 0x3397, + 0x0886: 0x339c, 0x0887: 0x33a1, 0x0888: 0x33a6, 0x0889: 0x33b5, 0x088a: 0x33ba, 0x088b: 0x33bf, + 0x088c: 0x33c4, 0x088d: 0x34f6, 0x088e: 0x33d3, 0x088f: 0x33d8, 0x0890: 0x33dd, 0x0891: 0x33e2, + 0x0892: 0x33f1, 0x0893: 0x33f6, 0x0894: 0x33fb, 0x0895: 0x3400, 0x0896: 0x34fb, 0x0897: 0x340f, + 0x0898: 0x3414, 0x0899: 0x3500, 0x089a: 0x3423, 0x089b: 0x3428, 0x089c: 0x342d, 0x089d: 0x3432, + 0x089e: 0x3505, 0x089f: 0x3285, 0x08a0: 0x34e0, 0x08a1: 0x32a2, 0x08a2: 0x34e7, 0x08a3: 0x32c0, + 0x08a4: 0x34ec, 0x08a5: 0x32d4, 0x08a6: 0x350a, 0x08a7: 0x3315, 0x08a8: 0x350f, 0x08a9: 0x3514, + 0x08aa: 0x3519, 0x08ab: 0x33a1, 0x08ac: 0x33a6, 0x08ad: 0x33c4, 0x08ae: 0x3400, 0x08af: 0x34fb, + 0x08b0: 0x3432, 0x08b1: 0x3505, 0x08b2: 0x351e, 0x08b3: 0x3525, 0x08b4: 0x352c, 0x08b5: 0x3533, + 0x08b6: 0x3538, 0x08b7: 0x353d, 0x08b8: 0x3542, 0x08b9: 0x3547, 0x08ba: 0x354c, 0x08bb: 0x3551, + 0x08bc: 0x3556, 0x08bd: 0x355b, 0x08be: 0x3560, 0x08bf: 0x3565, // Block 0x23, offset 0x8c0 - 0x08f3: 0x080a, + 0x08c0: 0x16c7, 0x08c1: 0x391e, 0x08c2: 0x3922, 0x08c3: 0x3926, 0x08c4: 0x392a, + 0x08c7: 0x392e, 0x08c8: 0x3930, 0x08c9: 0x146c, 0x08ca: 0x146c, 0x08cb: 0x146c, + 0x08cc: 0x146c, 0x08cd: 0x3900, 0x08ce: 0x3900, 0x08cf: 0x3900, 0x08d0: 0x38e0, 0x08d1: 0x38e2, + 0x08d2: 0x143e, 0x08d4: 0x04e1, 0x08d5: 0x38ea, 0x08d6: 0x38ee, 0x08d7: 0x38ec, + 0x08d8: 0x38f8, 0x08d9: 0x149c, 0x08da: 0x149e, 0x08db: 0x3902, 0x08dc: 0x3904, 0x08dd: 0x3906, + 0x08de: 0x390a, 0x08df: 0x3932, 0x08e0: 0x3934, 0x08e1: 0x3936, 0x08e2: 0x1494, 0x08e3: 0x3938, + 0x08e4: 0x393a, 0x08e5: 0x393c, 0x08e6: 0x149a, 0x08e8: 0x393e, 0x08e9: 0x3940, + 0x08ea: 0x3942, 0x08eb: 0x3944, + 0x08f0: 0x3946, 0x08f1: 0x394a, 0x08f2: 0x394f, 0x08f4: 0x3953, + 0x08f6: 0x3957, 0x08f7: 0x395b, 0x08f8: 0x3960, 0x08f9: 0x3964, 0x08fa: 0x3969, 0x08fb: 0x396d, + 0x08fc: 0x3972, 0x08fd: 0x3976, 0x08fe: 0x397b, 0x08ff: 0x397f, // Block 0x24, offset 0x900 - 0x091c: 0x0811, 0x091d: 0x0818, + 0x0900: 0x3984, 0x0901: 0x068d, 0x0902: 0x068d, 0x0903: 0x0692, 0x0904: 0x0692, 0x0905: 0x0697, + 0x0906: 0x0697, 0x0907: 0x069c, 0x0908: 0x069c, 0x0909: 0x06a1, 0x090a: 0x06a1, 0x090b: 0x06a1, + 0x090c: 0x06a1, 0x090d: 0x3987, 0x090e: 0x3987, 0x090f: 0x398a, 0x0910: 0x398a, 0x0911: 0x398a, + 0x0912: 0x398a, 0x0913: 0x398d, 0x0914: 0x398d, 0x0915: 0x3990, 0x0916: 0x3990, 0x0917: 0x3990, + 0x0918: 0x3990, 0x0919: 0x3993, 0x091a: 0x3993, 0x091b: 0x3993, 0x091c: 0x3993, 0x091d: 0x3996, + 0x091e: 0x3996, 0x091f: 0x3996, 0x0920: 0x3996, 0x0921: 0x3999, 0x0922: 0x3999, 0x0923: 0x3999, + 0x0924: 0x3999, 0x0925: 0x399c, 0x0926: 0x399c, 0x0927: 0x399c, 0x0928: 0x399c, 0x0929: 0x399f, + 0x092a: 0x399f, 0x092b: 0x39a2, 0x092c: 0x39a2, 0x092d: 0x39a5, 0x092e: 0x39a5, 0x092f: 0x39a8, + 0x0930: 0x39a8, 0x0931: 0x39ab, 0x0932: 0x39ab, 0x0933: 0x39ab, 0x0934: 0x39ab, 0x0935: 0x39ae, + 0x0936: 0x39ae, 0x0937: 0x39ae, 0x0938: 0x39ae, 0x0939: 0x39b1, 0x093a: 0x39b1, 0x093b: 0x39b1, + 0x093c: 0x39b1, 0x093d: 0x39b4, 0x093e: 0x39b4, 0x093f: 0x39b4, // Block 0x25, offset 0x940 - 0x094c: 0x081f, + 0x0940: 0x39b4, 0x0941: 0x39b7, 0x0942: 0x39b7, 0x0943: 0x39b7, 0x0944: 0x39b7, 0x0945: 0x39ba, + 0x0946: 0x39ba, 0x0947: 0x39ba, 0x0948: 0x39ba, 0x0949: 0x39bd, 0x094a: 0x39bd, 0x094b: 0x39bd, + 0x094c: 0x39bd, 0x094d: 0x39c0, 0x094e: 0x39c0, 0x094f: 0x39c0, 0x0950: 0x39c0, 0x0951: 0x39c3, + 0x0952: 0x39c3, 0x0953: 0x39c3, 0x0954: 0x39c3, 0x0955: 0x39c6, 0x0956: 0x39c6, 0x0957: 0x39c6, + 0x0958: 0x39c6, 0x0959: 0x39c9, 0x095a: 0x39c9, 0x095b: 0x39c9, 0x095c: 0x39c9, 0x095d: 0x39cc, + 0x095e: 0x39cc, 0x095f: 0x39cc, 0x0960: 0x39cc, 0x0961: 0x39cf, 0x0962: 0x39cf, 0x0963: 0x39cf, + 0x0964: 0x39cf, 0x0965: 0x39d2, 0x0966: 0x39d2, 0x0967: 0x39d2, 0x0968: 0x39d2, 0x0969: 0x39d5, + 0x096a: 0x39d5, 0x096b: 0x39d5, 0x096c: 0x39d5, 0x096d: 0x39d8, 0x096e: 0x39d8, 0x096f: 0x3239, + 0x0970: 0x3239, 0x0971: 0x39db, 0x0972: 0x39db, 0x0973: 0x39db, 0x0974: 0x39db, 0x0975: 0x39de, + 0x0976: 0x39de, 0x0977: 0x39e5, 0x0978: 0x39e5, 0x0979: 0x39ec, 0x097a: 0x39ec, 0x097b: 0x39f3, + 0x097c: 0x39f3, // Block 0x26, offset 0x980 - 0x0983: 0x0823, - 0x098d: 0x082a, - 0x0992: 0x0831, 0x0997: 0x0838, - 0x099c: 0x083f, - 0x09a9: 0x0846, - 0x09b3: 0x084d, 0x09b5: 0x0854, - 0x09b6: 0x085b, 0x09b7: 0x0862, 0x09b8: 0x086c, 0x09b9: 0x0873, + 0x0981: 0x38ec, 0x0982: 0x39f8, 0x0983: 0x3932, 0x0984: 0x3940, 0x0985: 0x3942, + 0x0986: 0x3934, 0x0987: 0x39fa, 0x0988: 0x149c, 0x0989: 0x149e, 0x098a: 0x3936, 0x098b: 0x1494, + 0x098c: 0x38e0, 0x098d: 0x3938, 0x098e: 0x143e, 0x098f: 0x39fc, 0x0990: 0x1486, 0x0991: 0x001c, + 0x0992: 0x000d, 0x0993: 0x000f, 0x0994: 0x1488, 0x0995: 0x148a, 0x0996: 0x148c, 0x0997: 0x148e, + 0x0998: 0x1490, 0x0999: 0x1492, 0x099a: 0x38ea, 0x099b: 0x04e1, 0x099c: 0x393a, 0x099d: 0x149a, + 0x099e: 0x393c, 0x099f: 0x38ee, 0x09a0: 0x3944, 0x09a1: 0x0906, 0x09a2: 0x090b, 0x09a3: 0x14ad, + 0x09a4: 0x090d, 0x09a5: 0x090f, 0x09a6: 0x14d9, 0x09a7: 0x0914, 0x09a8: 0x0916, 0x09a9: 0x0918, + 0x09aa: 0x091a, 0x09ab: 0x091c, 0x09ac: 0x091e, 0x09ad: 0x0920, 0x09ae: 0x0922, 0x09af: 0x0924, + 0x09b0: 0x0929, 0x09b1: 0x14c8, 0x09b2: 0x092b, 0x09b3: 0x17f6, 0x09b4: 0x092d, 0x09b5: 0x092f, + 0x09b6: 0x155f, 0x09b7: 0x0931, 0x09b8: 0x1570, 0x09b9: 0x17f8, 0x09ba: 0x14d4, 0x09bb: 0x392e, + 0x09bc: 0x393e, 0x09bd: 0x3930, 0x09be: 0x39fe, 0x09bf: 0x3900, // Block 0x27, offset 0x9c0 - 0x09c1: 0x087d, - 0x09d3: 0x0884, - 0x09dd: 0x088b, - 0x09e2: 0x0892, - 0x09e7: 0x0899, - 0x09ec: 0x08a0, - 0x09f9: 0x08a7, + 0x09c0: 0x13f7, 0x09c1: 0x0007, 0x09c2: 0x093d, 0x09c3: 0x0984, 0x09c4: 0x093f, 0x09c5: 0x0941, + 0x09c6: 0x098c, 0x09c7: 0x094c, 0x09c8: 0x0494, 0x09c9: 0x097c, 0x09ca: 0x0499, 0x09cb: 0x094e, + 0x09cc: 0x04c5, 0x09cd: 0x0950, 0x09ce: 0x14a0, 0x09cf: 0x001e, 0x09d0: 0x0960, 0x09d1: 0x17fa, + 0x09d2: 0x049b, 0x09d3: 0x02c8, 0x09d4: 0x0962, 0x09d5: 0x0964, 0x09d6: 0x096d, 0x09d7: 0x04a6, + 0x09d8: 0x04c7, 0x09d9: 0x04a8, 0x09da: 0x09df, 0x09db: 0x3902, 0x09dc: 0x3a00, 0x09dd: 0x3904, + 0x09de: 0x3a02, 0x09df: 0x3a04, 0x09e0: 0x3a08, 0x09e1: 0x38e6, 0x09e2: 0x391e, 0x09e3: 0x3922, + 0x09e4: 0x38e2, 0x09e5: 0x3a0c, 0x09e6: 0x2321, 0x09e7: 0x3a10, 0x09e8: 0x3a14, 0x09e9: 0x3a18, + 0x09ea: 0x3a1c, 0x09eb: 0x3a20, 0x09ec: 0x3a24, 0x09ed: 0x3a28, 0x09ee: 0x3a2c, 0x09ef: 0x3a30, + 0x09f0: 0x3a34, 0x09f1: 0x2269, 0x09f2: 0x226d, 0x09f3: 0x2271, 0x09f4: 0x2275, 0x09f5: 0x2279, + 0x09f6: 0x227d, 0x09f7: 0x2281, 0x09f8: 0x2285, 0x09f9: 0x2289, 0x09fa: 0x228d, 0x09fb: 0x2291, + 0x09fc: 0x2295, 0x09fd: 0x2299, 0x09fe: 0x229d, 0x09ff: 0x22a1, // Block 0x28, offset 0xa00 - 0x0a26: 0x08ae, + 0x0a00: 0x0906, 0x0a01: 0x090b, 0x0a02: 0x14ad, 0x0a03: 0x090d, 0x0a04: 0x090f, 0x0a05: 0x14d9, + 0x0a06: 0x0914, 0x0a07: 0x0916, 0x0a08: 0x0918, 0x0a09: 0x091a, 0x0a0a: 0x091c, 0x0a0b: 0x091e, + 0x0a0c: 0x0920, 0x0a0d: 0x0922, 0x0a0e: 0x0924, 0x0a0f: 0x0929, 0x0a10: 0x14c8, 0x0a11: 0x092b, + 0x0a12: 0x17f6, 0x0a13: 0x092d, 0x0a14: 0x092f, 0x0a15: 0x155f, 0x0a16: 0x0931, 0x0a17: 0x1570, + 0x0a18: 0x17f8, 0x0a19: 0x14d4, 0x0a1a: 0x0007, 0x0a1b: 0x093d, 0x0a1c: 0x0984, 0x0a1d: 0x093f, + 0x0a1e: 0x0941, 0x0a1f: 0x098c, 0x0a20: 0x094c, 0x0a21: 0x0494, 0x0a22: 0x097c, 0x0a23: 0x0499, + 0x0a24: 0x094e, 0x0a25: 0x04c5, 0x0a26: 0x0950, 0x0a27: 0x14a0, 0x0a28: 0x001e, 0x0a29: 0x0960, + 0x0a2a: 0x17fa, 0x0a2b: 0x049b, 0x0a2c: 0x02c8, 0x0a2d: 0x0962, 0x0a2e: 0x0964, 0x0a2f: 0x096d, + 0x0a30: 0x04a6, 0x0a31: 0x04c7, 0x0a32: 0x04a8, 0x0a33: 0x09df, 0x0a34: 0x0906, 0x0a35: 0x090b, + 0x0a36: 0x14ad, 0x0a37: 0x090d, 0x0a38: 0x090f, 0x0a39: 0x14d9, 0x0a3a: 0x0914, 0x0a3b: 0x0916, + 0x0a3c: 0x0918, 0x0a3d: 0x091a, 0x0a3e: 0x091c, 0x0a3f: 0x091e, // Block 0x29, offset 0xa40 - 0x0a7c: 0x08b5, + 0x0a40: 0x0920, 0x0a41: 0x0922, 0x0a42: 0x0924, 0x0a43: 0x0929, 0x0a44: 0x14c8, 0x0a45: 0x092b, + 0x0a46: 0x17f6, 0x0a47: 0x092d, 0x0a48: 0x092f, 0x0a49: 0x155f, 0x0a4a: 0x0931, 0x0a4b: 0x1570, + 0x0a4c: 0x17f8, 0x0a4d: 0x14d4, 0x0a4e: 0x0007, 0x0a4f: 0x093d, 0x0a50: 0x0984, 0x0a51: 0x093f, + 0x0a52: 0x0941, 0x0a53: 0x098c, 0x0a54: 0x094c, 0x0a56: 0x097c, 0x0a57: 0x0499, + 0x0a58: 0x094e, 0x0a59: 0x04c5, 0x0a5a: 0x0950, 0x0a5b: 0x14a0, 0x0a5c: 0x001e, 0x0a5d: 0x0960, + 0x0a5e: 0x17fa, 0x0a5f: 0x049b, 0x0a60: 0x02c8, 0x0a61: 0x0962, 0x0a62: 0x0964, 0x0a63: 0x096d, + 0x0a64: 0x04a6, 0x0a65: 0x04c7, 0x0a66: 0x04a8, 0x0a67: 0x09df, 0x0a68: 0x0906, 0x0a69: 0x090b, + 0x0a6a: 0x14ad, 0x0a6b: 0x090d, 0x0a6c: 0x090f, 0x0a6d: 0x14d9, 0x0a6e: 0x0914, 0x0a6f: 0x0916, + 0x0a70: 0x0918, 0x0a71: 0x091a, 0x0a72: 0x091c, 0x0a73: 0x091e, 0x0a74: 0x0920, 0x0a75: 0x0922, + 0x0a76: 0x0924, 0x0a77: 0x0929, 0x0a78: 0x14c8, 0x0a79: 0x092b, 0x0a7a: 0x17f6, 0x0a7b: 0x092d, + 0x0a7c: 0x092f, 0x0a7d: 0x155f, 0x0a7e: 0x0931, 0x0a7f: 0x1570, // Block 0x2a, offset 0xa80 - 0x0a86: 0x08b9, 0x0a88: 0x08c0, 0x0a8a: 0x08c7, - 0x0a8c: 0x08ce, 0x0a8e: 0x08d5, - 0x0a92: 0x08dc, - 0x0abb: 0x08e3, - 0x0abd: 0x08ea, + 0x0a80: 0x17f8, 0x0a81: 0x14d4, 0x0a82: 0x0007, 0x0a83: 0x093d, 0x0a84: 0x0984, 0x0a85: 0x093f, + 0x0a86: 0x0941, 0x0a87: 0x098c, 0x0a88: 0x094c, 0x0a89: 0x0494, 0x0a8a: 0x097c, 0x0a8b: 0x0499, + 0x0a8c: 0x094e, 0x0a8d: 0x04c5, 0x0a8e: 0x0950, 0x0a8f: 0x14a0, 0x0a90: 0x001e, 0x0a91: 0x0960, + 0x0a92: 0x17fa, 0x0a93: 0x049b, 0x0a94: 0x02c8, 0x0a95: 0x0962, 0x0a96: 0x0964, 0x0a97: 0x096d, + 0x0a98: 0x04a6, 0x0a99: 0x04c7, 0x0a9a: 0x04a8, 0x0a9b: 0x09df, 0x0a9c: 0x0906, + 0x0a9e: 0x14ad, 0x0a9f: 0x090d, 0x0aa2: 0x0914, + 0x0aa5: 0x091a, 0x0aa6: 0x091c, 0x0aa9: 0x0922, + 0x0aaa: 0x0924, 0x0aab: 0x0929, 0x0aac: 0x14c8, 0x0aae: 0x17f6, 0x0aaf: 0x092d, + 0x0ab0: 0x092f, 0x0ab1: 0x155f, 0x0ab2: 0x0931, 0x0ab3: 0x1570, 0x0ab4: 0x17f8, 0x0ab5: 0x14d4, + 0x0ab6: 0x0007, 0x0ab7: 0x093d, 0x0ab8: 0x0984, 0x0ab9: 0x093f, 0x0abb: 0x098c, + 0x0abd: 0x0494, 0x0abe: 0x097c, 0x0abf: 0x0499, // Block 0x2b, offset 0xac0 - 0x0ac0: 0x08f1, 0x0ac1: 0x08f8, 0x0ac3: 0x08ff, + 0x0ac0: 0x094e, 0x0ac1: 0x04c5, 0x0ac2: 0x0950, 0x0ac3: 0x14a0, 0x0ac5: 0x0960, + 0x0ac6: 0x17fa, 0x0ac7: 0x049b, 0x0ac8: 0x02c8, 0x0ac9: 0x0962, 0x0aca: 0x0964, 0x0acb: 0x096d, + 0x0acc: 0x04a6, 0x0acd: 0x04c7, 0x0ace: 0x04a8, 0x0acf: 0x09df, 0x0ad0: 0x0906, 0x0ad1: 0x090b, + 0x0ad2: 0x14ad, 0x0ad3: 0x090d, 0x0ad4: 0x090f, 0x0ad5: 0x14d9, 0x0ad6: 0x0914, 0x0ad7: 0x0916, + 0x0ad8: 0x0918, 0x0ad9: 0x091a, 0x0ada: 0x091c, 0x0adb: 0x091e, 0x0adc: 0x0920, 0x0add: 0x0922, + 0x0ade: 0x0924, 0x0adf: 0x0929, 0x0ae0: 0x14c8, 0x0ae1: 0x092b, 0x0ae2: 0x17f6, 0x0ae3: 0x092d, + 0x0ae4: 0x092f, 0x0ae5: 0x155f, 0x0ae6: 0x0931, 0x0ae7: 0x1570, 0x0ae8: 0x17f8, 0x0ae9: 0x14d4, + 0x0aea: 0x0007, 0x0aeb: 0x093d, 0x0aec: 0x0984, 0x0aed: 0x093f, 0x0aee: 0x0941, 0x0aef: 0x098c, + 0x0af0: 0x094c, 0x0af1: 0x0494, 0x0af2: 0x097c, 0x0af3: 0x0499, 0x0af4: 0x094e, 0x0af5: 0x04c5, + 0x0af6: 0x0950, 0x0af7: 0x14a0, 0x0af8: 0x001e, 0x0af9: 0x0960, 0x0afa: 0x17fa, 0x0afb: 0x049b, + 0x0afc: 0x02c8, 0x0afd: 0x0962, 0x0afe: 0x0964, 0x0aff: 0x096d, // Block 0x2c, offset 0xb00 - 0x0b2c: 0x0906, 0x0b2d: 0x0908, 0x0b2e: 0x090b, - 0x0b30: 0x090d, 0x0b31: 0x090f, 0x0b32: 0x0911, 0x0b33: 0x0914, 0x0b34: 0x0916, 0x0b35: 0x0918, - 0x0b36: 0x091a, 0x0b37: 0x091c, 0x0b38: 0x091e, 0x0b39: 0x0920, 0x0b3a: 0x0922, - 0x0b3c: 0x0924, 0x0b3d: 0x0926, 0x0b3e: 0x0929, 0x0b3f: 0x092b, + 0x0b00: 0x04a6, 0x0b01: 0x04c7, 0x0b02: 0x04a8, 0x0b03: 0x09df, 0x0b04: 0x0906, 0x0b05: 0x090b, + 0x0b07: 0x090d, 0x0b08: 0x090f, 0x0b09: 0x14d9, 0x0b0a: 0x0914, + 0x0b0d: 0x091a, 0x0b0e: 0x091c, 0x0b0f: 0x091e, 0x0b10: 0x0920, 0x0b11: 0x0922, + 0x0b12: 0x0924, 0x0b13: 0x0929, 0x0b14: 0x14c8, 0x0b16: 0x17f6, 0x0b17: 0x092d, + 0x0b18: 0x092f, 0x0b19: 0x155f, 0x0b1a: 0x0931, 0x0b1b: 0x1570, 0x0b1c: 0x17f8, + 0x0b1e: 0x0007, 0x0b1f: 0x093d, 0x0b20: 0x0984, 0x0b21: 0x093f, 0x0b22: 0x0941, 0x0b23: 0x098c, + 0x0b24: 0x094c, 0x0b25: 0x0494, 0x0b26: 0x097c, 0x0b27: 0x0499, 0x0b28: 0x094e, 0x0b29: 0x04c5, + 0x0b2a: 0x0950, 0x0b2b: 0x14a0, 0x0b2c: 0x001e, 0x0b2d: 0x0960, 0x0b2e: 0x17fa, 0x0b2f: 0x049b, + 0x0b30: 0x02c8, 0x0b31: 0x0962, 0x0b32: 0x0964, 0x0b33: 0x096d, 0x0b34: 0x04a6, 0x0b35: 0x04c7, + 0x0b36: 0x04a8, 0x0b37: 0x09df, 0x0b38: 0x0906, 0x0b39: 0x090b, 0x0b3b: 0x090d, + 0x0b3c: 0x090f, 0x0b3d: 0x14d9, 0x0b3e: 0x0914, // Block 0x2d, offset 0xb40 - 0x0b40: 0x092d, 0x0b41: 0x092f, 0x0b42: 0x0931, 0x0b43: 0x0007, 0x0b44: 0x0933, 0x0b45: 0x0936, - 0x0b46: 0x0939, 0x0b47: 0x093d, 0x0b48: 0x093f, 0x0b49: 0x0941, 0x0b4a: 0x0943, 0x0b4b: 0x0946, - 0x0b4c: 0x0949, 0x0b4d: 0x094c, 0x0b4f: 0x094e, 0x0b50: 0x0950, 0x0b51: 0x0952, - 0x0b52: 0x001e, 0x0b53: 0x0955, 0x0b54: 0x0958, 0x0b55: 0x095c, 0x0b56: 0x0960, 0x0b57: 0x0962, - 0x0b58: 0x0964, 0x0b59: 0x0966, 0x0b5a: 0x096a, 0x0b5b: 0x096d, 0x0b5c: 0x096f, 0x0b5d: 0x0559, - 0x0b5e: 0x0973, 0x0b5f: 0x0976, 0x0b60: 0x056c, 0x0b61: 0x0979, 0x0b62: 0x097c, 0x0b63: 0x049b, - 0x0b64: 0x0964, 0x0b65: 0x096d, 0x0b66: 0x0559, 0x0b67: 0x0973, 0x0b68: 0x0575, 0x0b69: 0x056c, - 0x0b6a: 0x0979, - 0x0b78: 0x097e, + 0x0b40: 0x0918, 0x0b41: 0x091a, 0x0b42: 0x091c, 0x0b43: 0x091e, 0x0b44: 0x0920, + 0x0b46: 0x0924, 0x0b4a: 0x17f6, 0x0b4b: 0x092d, + 0x0b4c: 0x092f, 0x0b4d: 0x155f, 0x0b4e: 0x0931, 0x0b4f: 0x1570, 0x0b50: 0x17f8, + 0x0b52: 0x0007, 0x0b53: 0x093d, 0x0b54: 0x0984, 0x0b55: 0x093f, 0x0b56: 0x0941, 0x0b57: 0x098c, + 0x0b58: 0x094c, 0x0b59: 0x0494, 0x0b5a: 0x097c, 0x0b5b: 0x0499, 0x0b5c: 0x094e, 0x0b5d: 0x04c5, + 0x0b5e: 0x0950, 0x0b5f: 0x14a0, 0x0b60: 0x001e, 0x0b61: 0x0960, 0x0b62: 0x17fa, 0x0b63: 0x049b, + 0x0b64: 0x02c8, 0x0b65: 0x0962, 0x0b66: 0x0964, 0x0b67: 0x096d, 0x0b68: 0x04a6, 0x0b69: 0x04c7, + 0x0b6a: 0x04a8, 0x0b6b: 0x09df, 0x0b6c: 0x0906, 0x0b6d: 0x090b, 0x0b6e: 0x14ad, 0x0b6f: 0x090d, + 0x0b70: 0x090f, 0x0b71: 0x14d9, 0x0b72: 0x0914, 0x0b73: 0x0916, 0x0b74: 0x0918, 0x0b75: 0x091a, + 0x0b76: 0x091c, 0x0b77: 0x091e, 0x0b78: 0x0920, 0x0b79: 0x0922, 0x0b7a: 0x0924, 0x0b7b: 0x0929, + 0x0b7c: 0x14c8, 0x0b7d: 0x092b, 0x0b7e: 0x17f6, 0x0b7f: 0x092d, // Block 0x2e, offset 0xb80 - 0x0b9b: 0x0981, 0x0b9c: 0x0984, 0x0b9d: 0x0986, - 0x0b9e: 0x0989, 0x0b9f: 0x0949, 0x0ba0: 0x098c, 0x0ba1: 0x098e, 0x0ba2: 0x0991, 0x0ba3: 0x0994, - 0x0ba4: 0x0997, 0x0ba5: 0x099a, 0x0ba6: 0x099d, 0x0ba7: 0x09a0, 0x0ba8: 0x09a4, 0x0ba9: 0x09a7, - 0x0baa: 0x09aa, 0x0bab: 0x09ae, 0x0bac: 0x09b1, 0x0bad: 0x09b4, 0x0bae: 0x09b7, 0x0baf: 0x09ba, - 0x0bb0: 0x09bd, 0x0bb1: 0x09c0, 0x0bb2: 0x09c3, 0x0bb3: 0x09c6, 0x0bb4: 0x09c9, 0x0bb5: 0x09cc, - 0x0bb6: 0x09cf, 0x0bb7: 0x09d2, 0x0bb8: 0x09d5, 0x0bb9: 0x09d9, 0x0bba: 0x09dc, 0x0bbb: 0x09df, - 0x0bbc: 0x09e1, 0x0bbd: 0x09e4, 0x0bbe: 0x09e7, 0x0bbf: 0x055c, + 0x0b80: 0x092f, 0x0b81: 0x155f, 0x0b82: 0x0931, 0x0b83: 0x1570, 0x0b84: 0x17f8, 0x0b85: 0x14d4, + 0x0b86: 0x0007, 0x0b87: 0x093d, 0x0b88: 0x0984, 0x0b89: 0x093f, 0x0b8a: 0x0941, 0x0b8b: 0x098c, + 0x0b8c: 0x094c, 0x0b8d: 0x0494, 0x0b8e: 0x097c, 0x0b8f: 0x0499, 0x0b90: 0x094e, 0x0b91: 0x04c5, + 0x0b92: 0x0950, 0x0b93: 0x14a0, 0x0b94: 0x001e, 0x0b95: 0x0960, 0x0b96: 0x17fa, 0x0b97: 0x049b, + 0x0b98: 0x02c8, 0x0b99: 0x0962, 0x0b9a: 0x0964, 0x0b9b: 0x096d, 0x0b9c: 0x04a6, 0x0b9d: 0x04c7, + 0x0b9e: 0x04a8, 0x0b9f: 0x09df, 0x0ba0: 0x0906, 0x0ba1: 0x090b, 0x0ba2: 0x14ad, 0x0ba3: 0x090d, + 0x0ba4: 0x090f, 0x0ba5: 0x14d9, 0x0ba6: 0x0914, 0x0ba7: 0x0916, 0x0ba8: 0x0918, 0x0ba9: 0x091a, + 0x0baa: 0x091c, 0x0bab: 0x091e, 0x0bac: 0x0920, 0x0bad: 0x0922, 0x0bae: 0x0924, 0x0baf: 0x0929, + 0x0bb0: 0x14c8, 0x0bb1: 0x092b, 0x0bb2: 0x17f6, 0x0bb3: 0x092d, 0x0bb4: 0x092f, 0x0bb5: 0x155f, + 0x0bb6: 0x0931, 0x0bb7: 0x1570, 0x0bb8: 0x17f8, 0x0bb9: 0x14d4, 0x0bba: 0x0007, 0x0bbb: 0x093d, + 0x0bbc: 0x0984, 0x0bbd: 0x093f, 0x0bbe: 0x0941, 0x0bbf: 0x098c, // Block 0x2f, offset 0xbc0 - 0x0bc0: 0x09ea, 0x0bc1: 0x09ee, 0x0bc2: 0x09f2, 0x0bc3: 0x09f6, 0x0bc4: 0x09fa, 0x0bc5: 0x09fe, - 0x0bc6: 0x0a02, 0x0bc7: 0x0a06, 0x0bc8: 0x0a0a, 0x0bc9: 0x0a10, 0x0bca: 0x0a16, 0x0bcb: 0x0a1a, - 0x0bcc: 0x0a1e, 0x0bcd: 0x0a22, 0x0bce: 0x0a26, 0x0bcf: 0x0a2a, 0x0bd0: 0x0a2e, 0x0bd1: 0x0a32, - 0x0bd2: 0x0a36, 0x0bd3: 0x0a3a, 0x0bd4: 0x0a3e, 0x0bd5: 0x0a44, 0x0bd6: 0x0a4a, 0x0bd7: 0x0a50, - 0x0bd8: 0x0a56, 0x0bd9: 0x0a5a, 0x0bda: 0x0a5e, 0x0bdb: 0x0a62, 0x0bdc: 0x0a66, 0x0bdd: 0x0a6c, - 0x0bde: 0x0a72, 0x0bdf: 0x0a76, 0x0be0: 0x0a7a, 0x0be1: 0x0a7e, 0x0be2: 0x0a82, 0x0be3: 0x0a86, - 0x0be4: 0x0a8a, 0x0be5: 0x0a8e, 0x0be6: 0x0a92, 0x0be7: 0x0a96, 0x0be8: 0x0a9a, 0x0be9: 0x0a9e, - 0x0bea: 0x0aa2, 0x0beb: 0x0aa6, 0x0bec: 0x0aaa, 0x0bed: 0x0aae, 0x0bee: 0x0ab2, 0x0bef: 0x0ab8, - 0x0bf0: 0x0abe, 0x0bf1: 0x0ac2, 0x0bf2: 0x0ac6, 0x0bf3: 0x0aca, 0x0bf4: 0x0ace, 0x0bf5: 0x0ad2, - 0x0bf6: 0x0ad6, 0x0bf7: 0x0ada, 0x0bf8: 0x0ade, 0x0bf9: 0x0ae4, 0x0bfa: 0x0aea, 0x0bfb: 0x0aee, - 0x0bfc: 0x0af2, 0x0bfd: 0x0af6, 0x0bfe: 0x0afa, 0x0bff: 0x0afe, + 0x0bc0: 0x094c, 0x0bc1: 0x0494, 0x0bc2: 0x097c, 0x0bc3: 0x0499, 0x0bc4: 0x094e, 0x0bc5: 0x04c5, + 0x0bc6: 0x0950, 0x0bc7: 0x14a0, 0x0bc8: 0x001e, 0x0bc9: 0x0960, 0x0bca: 0x17fa, 0x0bcb: 0x049b, + 0x0bcc: 0x02c8, 0x0bcd: 0x0962, 0x0bce: 0x0964, 0x0bcf: 0x096d, 0x0bd0: 0x04a6, 0x0bd1: 0x04c7, + 0x0bd2: 0x04a8, 0x0bd3: 0x09df, 0x0bd4: 0x0906, 0x0bd5: 0x090b, 0x0bd6: 0x14ad, 0x0bd7: 0x090d, + 0x0bd8: 0x090f, 0x0bd9: 0x14d9, 0x0bda: 0x0914, 0x0bdb: 0x0916, 0x0bdc: 0x0918, 0x0bdd: 0x091a, + 0x0bde: 0x091c, 0x0bdf: 0x091e, 0x0be0: 0x0920, 0x0be1: 0x0922, 0x0be2: 0x0924, 0x0be3: 0x0929, + 0x0be4: 0x14c8, 0x0be5: 0x092b, 0x0be6: 0x17f6, 0x0be7: 0x092d, 0x0be8: 0x092f, 0x0be9: 0x155f, + 0x0bea: 0x0931, 0x0beb: 0x1570, 0x0bec: 0x17f8, 0x0bed: 0x14d4, 0x0bee: 0x0007, 0x0bef: 0x093d, + 0x0bf0: 0x0984, 0x0bf1: 0x093f, 0x0bf2: 0x0941, 0x0bf3: 0x098c, 0x0bf4: 0x094c, 0x0bf5: 0x0494, + 0x0bf6: 0x097c, 0x0bf7: 0x0499, 0x0bf8: 0x094e, 0x0bf9: 0x04c5, 0x0bfa: 0x0950, 0x0bfb: 0x14a0, + 0x0bfc: 0x001e, 0x0bfd: 0x0960, 0x0bfe: 0x17fa, 0x0bff: 0x049b, // Block 0x30, offset 0xc00 - 0x0c00: 0x0b02, 0x0c01: 0x0b06, 0x0c02: 0x0b0a, 0x0c03: 0x0b0e, 0x0c04: 0x0b12, 0x0c05: 0x0b16, - 0x0c06: 0x0b1a, 0x0c07: 0x0b1e, 0x0c08: 0x0b22, 0x0c09: 0x0b26, 0x0c0a: 0x0b2a, 0x0c0b: 0x0b2e, - 0x0c0c: 0x0b32, 0x0c0d: 0x0b38, 0x0c0e: 0x0b3e, 0x0c0f: 0x0b44, 0x0c10: 0x0b4a, 0x0c11: 0x0b50, - 0x0c12: 0x0b56, 0x0c13: 0x0b5c, 0x0c14: 0x0b62, 0x0c15: 0x0b66, 0x0c16: 0x0b6a, 0x0c17: 0x0b6e, - 0x0c18: 0x0b72, 0x0c19: 0x0b76, 0x0c1a: 0x0b7a, 0x0c1b: 0x0b7e, 0x0c1c: 0x0b82, 0x0c1d: 0x0b88, - 0x0c1e: 0x0b8e, 0x0c1f: 0x0b92, 0x0c20: 0x0b96, 0x0c21: 0x0b9a, 0x0c22: 0x0b9e, 0x0c23: 0x0ba2, - 0x0c24: 0x0ba6, 0x0c25: 0x0bac, 0x0c26: 0x0bb2, 0x0c27: 0x0bb8, 0x0c28: 0x0bbe, 0x0c29: 0x0bc4, - 0x0c2a: 0x0bca, 0x0c2b: 0x0bce, 0x0c2c: 0x0bd2, 0x0c2d: 0x0bd6, 0x0c2e: 0x0bda, 0x0c2f: 0x0bde, - 0x0c30: 0x0be2, 0x0c31: 0x0be6, 0x0c32: 0x0bea, 0x0c33: 0x0bee, 0x0c34: 0x0bf2, 0x0c35: 0x0bf6, - 0x0c36: 0x0bfa, 0x0c37: 0x0bfe, 0x0c38: 0x0c02, 0x0c39: 0x0c08, 0x0c3a: 0x0c0e, 0x0c3b: 0x0c14, - 0x0c3c: 0x0c1a, 0x0c3d: 0x0c1e, 0x0c3e: 0x0c22, 0x0c3f: 0x0c26, + 0x0c00: 0x02c8, 0x0c01: 0x0962, 0x0c02: 0x0964, 0x0c03: 0x096d, 0x0c04: 0x04a6, 0x0c05: 0x04c7, + 0x0c06: 0x04a8, 0x0c07: 0x09df, 0x0c08: 0x0906, 0x0c09: 0x090b, 0x0c0a: 0x14ad, 0x0c0b: 0x090d, + 0x0c0c: 0x090f, 0x0c0d: 0x14d9, 0x0c0e: 0x0914, 0x0c0f: 0x0916, 0x0c10: 0x0918, 0x0c11: 0x091a, + 0x0c12: 0x091c, 0x0c13: 0x091e, 0x0c14: 0x0920, 0x0c15: 0x0922, 0x0c16: 0x0924, 0x0c17: 0x0929, + 0x0c18: 0x14c8, 0x0c19: 0x092b, 0x0c1a: 0x17f6, 0x0c1b: 0x092d, 0x0c1c: 0x092f, 0x0c1d: 0x155f, + 0x0c1e: 0x0931, 0x0c1f: 0x1570, 0x0c20: 0x17f8, 0x0c21: 0x14d4, 0x0c22: 0x0007, 0x0c23: 0x093d, + 0x0c24: 0x0984, 0x0c25: 0x093f, 0x0c26: 0x0941, 0x0c27: 0x098c, 0x0c28: 0x094c, 0x0c29: 0x0494, + 0x0c2a: 0x097c, 0x0c2b: 0x0499, 0x0c2c: 0x094e, 0x0c2d: 0x04c5, 0x0c2e: 0x0950, 0x0c2f: 0x14a0, + 0x0c30: 0x001e, 0x0c31: 0x0960, 0x0c32: 0x17fa, 0x0c33: 0x049b, 0x0c34: 0x02c8, 0x0c35: 0x0962, + 0x0c36: 0x0964, 0x0c37: 0x096d, 0x0c38: 0x04a6, 0x0c39: 0x04c7, 0x0c3a: 0x04a8, 0x0c3b: 0x09df, + 0x0c3c: 0x0906, 0x0c3d: 0x090b, 0x0c3e: 0x14ad, 0x0c3f: 0x090d, // Block 0x31, offset 0xc40 - 0x0c40: 0x0c2a, 0x0c41: 0x0c2e, 0x0c42: 0x0c32, 0x0c43: 0x0c36, 0x0c44: 0x0c3a, 0x0c45: 0x0c3e, - 0x0c46: 0x0c42, 0x0c47: 0x0c46, 0x0c48: 0x0c4a, 0x0c49: 0x0c4e, 0x0c4a: 0x0c52, 0x0c4b: 0x0c56, - 0x0c4c: 0x0c5a, 0x0c4d: 0x0c5e, 0x0c4e: 0x0c62, 0x0c4f: 0x0c66, 0x0c50: 0x0c6a, 0x0c51: 0x0c6e, - 0x0c52: 0x0c72, 0x0c53: 0x0c76, 0x0c54: 0x0c7a, 0x0c55: 0x0c7e, 0x0c56: 0x0c82, 0x0c57: 0x0c86, - 0x0c58: 0x0c8a, 0x0c59: 0x0c8e, 0x0c5a: 0x0c92, 0x0c5b: 0x0b9a, - 0x0c60: 0x0c9b, 0x0c61: 0x0c9f, 0x0c62: 0x0ca3, 0x0c63: 0x0ca7, - 0x0c64: 0x0cab, 0x0c65: 0x0cb1, 0x0c66: 0x0cb7, 0x0c67: 0x0cbd, 0x0c68: 0x0cc3, 0x0c69: 0x0cc9, - 0x0c6a: 0x0ccf, 0x0c6b: 0x0cd5, 0x0c6c: 0x0cdb, 0x0c6d: 0x0ce1, 0x0c6e: 0x0ce7, 0x0c6f: 0x0ced, - 0x0c70: 0x0cf3, 0x0c71: 0x0cf9, 0x0c72: 0x0cff, 0x0c73: 0x0d05, 0x0c74: 0x0d0b, 0x0c75: 0x0d11, - 0x0c76: 0x0d17, 0x0c77: 0x0d1d, 0x0c78: 0x0d23, 0x0c79: 0x0d27, 0x0c7a: 0x0d2b, 0x0c7b: 0x0d2f, - 0x0c7c: 0x0d33, 0x0c7d: 0x0d37, 0x0c7e: 0x0d3b, 0x0c7f: 0x0d41, + 0x0c40: 0x090f, 0x0c41: 0x14d9, 0x0c42: 0x0914, 0x0c43: 0x0916, 0x0c44: 0x0918, 0x0c45: 0x091a, + 0x0c46: 0x091c, 0x0c47: 0x091e, 0x0c48: 0x0920, 0x0c49: 0x0922, 0x0c4a: 0x0924, 0x0c4b: 0x0929, + 0x0c4c: 0x14c8, 0x0c4d: 0x092b, 0x0c4e: 0x17f6, 0x0c4f: 0x092d, 0x0c50: 0x092f, 0x0c51: 0x155f, + 0x0c52: 0x0931, 0x0c53: 0x1570, 0x0c54: 0x17f8, 0x0c55: 0x14d4, 0x0c56: 0x0007, 0x0c57: 0x093d, + 0x0c58: 0x0984, 0x0c59: 0x093f, 0x0c5a: 0x0941, 0x0c5b: 0x098c, 0x0c5c: 0x094c, 0x0c5d: 0x0494, + 0x0c5e: 0x097c, 0x0c5f: 0x0499, 0x0c60: 0x094e, 0x0c61: 0x04c5, 0x0c62: 0x0950, 0x0c63: 0x14a0, + 0x0c64: 0x001e, 0x0c65: 0x0960, 0x0c66: 0x17fa, 0x0c67: 0x049b, 0x0c68: 0x02c8, 0x0c69: 0x0962, + 0x0c6a: 0x0964, 0x0c6b: 0x096d, 0x0c6c: 0x04a6, 0x0c6d: 0x04c7, 0x0c6e: 0x04a8, 0x0c6f: 0x09df, + 0x0c70: 0x0906, 0x0c71: 0x090b, 0x0c72: 0x14ad, 0x0c73: 0x090d, 0x0c74: 0x090f, 0x0c75: 0x14d9, + 0x0c76: 0x0914, 0x0c77: 0x0916, 0x0c78: 0x0918, 0x0c79: 0x091a, 0x0c7a: 0x091c, 0x0c7b: 0x091e, + 0x0c7c: 0x0920, 0x0c7d: 0x0922, 0x0c7e: 0x0924, 0x0c7f: 0x0929, // Block 0x32, offset 0xc80 - 0x0c80: 0x0d47, 0x0c81: 0x0d4d, 0x0c82: 0x0d53, 0x0c83: 0x0d59, 0x0c84: 0x0d5f, 0x0c85: 0x0d65, - 0x0c86: 0x0d6b, 0x0c87: 0x0d71, 0x0c88: 0x0d77, 0x0c89: 0x0d7b, 0x0c8a: 0x0d7f, 0x0c8b: 0x0d83, - 0x0c8c: 0x0d87, 0x0c8d: 0x0d8b, 0x0c8e: 0x0d8f, 0x0c8f: 0x0d93, 0x0c90: 0x0d97, 0x0c91: 0x0d9d, - 0x0c92: 0x0da3, 0x0c93: 0x0da9, 0x0c94: 0x0daf, 0x0c95: 0x0db5, 0x0c96: 0x0dbb, 0x0c97: 0x0dc1, - 0x0c98: 0x0dc7, 0x0c99: 0x0dcd, 0x0c9a: 0x0dd3, 0x0c9b: 0x0dd9, 0x0c9c: 0x0ddf, 0x0c9d: 0x0de5, - 0x0c9e: 0x0deb, 0x0c9f: 0x0df1, 0x0ca0: 0x0df7, 0x0ca1: 0x0dfd, 0x0ca2: 0x0e03, 0x0ca3: 0x0e09, - 0x0ca4: 0x0e0f, 0x0ca5: 0x0e13, 0x0ca6: 0x0e17, 0x0ca7: 0x0e1b, 0x0ca8: 0x0e1f, 0x0ca9: 0x0e25, - 0x0caa: 0x0e2b, 0x0cab: 0x0e31, 0x0cac: 0x0e37, 0x0cad: 0x0e3d, 0x0cae: 0x0e43, 0x0caf: 0x0e49, - 0x0cb0: 0x0e4f, 0x0cb1: 0x0e55, 0x0cb2: 0x0e5b, 0x0cb3: 0x0e5f, 0x0cb4: 0x0e63, 0x0cb5: 0x0e67, - 0x0cb6: 0x0e6b, 0x0cb7: 0x0e6f, 0x0cb8: 0x0e73, 0x0cb9: 0x0e77, + 0x0c80: 0x14c8, 0x0c81: 0x092b, 0x0c82: 0x17f6, 0x0c83: 0x092d, 0x0c84: 0x092f, 0x0c85: 0x155f, + 0x0c86: 0x0931, 0x0c87: 0x1570, 0x0c88: 0x17f8, 0x0c89: 0x14d4, 0x0c8a: 0x0007, 0x0c8b: 0x093d, + 0x0c8c: 0x0984, 0x0c8d: 0x093f, 0x0c8e: 0x0941, 0x0c8f: 0x098c, 0x0c90: 0x094c, 0x0c91: 0x0494, + 0x0c92: 0x097c, 0x0c93: 0x0499, 0x0c94: 0x094e, 0x0c95: 0x04c5, 0x0c96: 0x0950, 0x0c97: 0x14a0, + 0x0c98: 0x001e, 0x0c99: 0x0960, 0x0c9a: 0x17fa, 0x0c9b: 0x049b, 0x0c9c: 0x02c8, 0x0c9d: 0x0962, + 0x0c9e: 0x0964, 0x0c9f: 0x096d, 0x0ca0: 0x04a6, 0x0ca1: 0x04c7, 0x0ca2: 0x04a8, 0x0ca3: 0x09df, + 0x0ca4: 0x3b27, 0x0ca5: 0x3b2a, 0x0ca8: 0x3b2d, 0x0ca9: 0x3b30, + 0x0caa: 0x14eb, 0x0cab: 0x3b33, 0x0cac: 0x3b36, 0x0cad: 0x3b39, 0x0cae: 0x3b3c, 0x0caf: 0x057b, + 0x0cb0: 0x3b3f, 0x0cb1: 0x3b42, 0x0cb2: 0x3b45, 0x0cb3: 0x3b48, 0x0cb4: 0x3b4b, 0x0cb5: 0x3b4e, + 0x0cb6: 0x3b51, 0x0cb7: 0x14ee, 0x0cb8: 0x3b54, 0x0cb9: 0x057b, 0x0cba: 0x0581, 0x0cbb: 0x3b57, + 0x0cbc: 0x055f, 0x0cbd: 0x3b5a, 0x0cbe: 0x3b5d, 0x0cbf: 0x3b60, // Block 0x33, offset 0xcc0 - 0x0cc0: 0x0e7b, 0x0cc1: 0x0e80, 0x0cc2: 0x0e85, 0x0cc3: 0x0e8c, 0x0cc4: 0x0e93, 0x0cc5: 0x0e9a, - 0x0cc6: 0x0ea1, 0x0cc7: 0x0ea8, 0x0cc8: 0x0eaf, 0x0cc9: 0x0eb4, 0x0cca: 0x0eb9, 0x0ccb: 0x0ec0, - 0x0ccc: 0x0ec7, 0x0ccd: 0x0ece, 0x0cce: 0x0ed5, 0x0ccf: 0x0edc, 0x0cd0: 0x0ee3, 0x0cd1: 0x0ee8, - 0x0cd2: 0x0eed, 0x0cd3: 0x0ef4, 0x0cd4: 0x0efb, 0x0cd5: 0x0f02, - 0x0cd8: 0x0f09, 0x0cd9: 0x0f0e, 0x0cda: 0x0f13, 0x0cdb: 0x0f1a, 0x0cdc: 0x0f21, 0x0cdd: 0x0f28, - 0x0ce0: 0x0f2f, 0x0ce1: 0x0f34, 0x0ce2: 0x0f39, 0x0ce3: 0x0f40, - 0x0ce4: 0x0f47, 0x0ce5: 0x0f4e, 0x0ce6: 0x0f55, 0x0ce7: 0x0f5c, 0x0ce8: 0x0f63, 0x0ce9: 0x0f68, - 0x0cea: 0x0f6d, 0x0ceb: 0x0f74, 0x0cec: 0x0f7b, 0x0ced: 0x0f82, 0x0cee: 0x0f89, 0x0cef: 0x0f90, - 0x0cf0: 0x0f97, 0x0cf1: 0x0f9c, 0x0cf2: 0x0fa1, 0x0cf3: 0x0fa8, 0x0cf4: 0x0faf, 0x0cf5: 0x0fb6, - 0x0cf6: 0x0fbd, 0x0cf7: 0x0fc4, 0x0cf8: 0x0fcb, 0x0cf9: 0x0fd0, 0x0cfa: 0x0fd5, 0x0cfb: 0x0fdc, - 0x0cfc: 0x0fe3, 0x0cfd: 0x0fea, 0x0cfe: 0x0ff1, 0x0cff: 0x0ff8, + 0x0cc0: 0x14d6, 0x0cc1: 0x3b63, 0x0cc2: 0x3b67, 0x0cc3: 0x0559, 0x0cc4: 0x0973, 0x0cc5: 0x0976, + 0x0cc6: 0x057e, 0x0cc7: 0x3b6a, 0x0cc8: 0x3b6d, 0x0cc9: 0x055c, 0x0cca: 0x12fd, 0x0ccb: 0x0572, + 0x0ccc: 0x3b70, 0x0ccd: 0x0015, 0x0cce: 0x3b73, 0x0ccf: 0x3b76, 0x0cd0: 0x3b79, 0x0cd1: 0x056f, + 0x0cd2: 0x0575, 0x0cd3: 0x0578, 0x0cd4: 0x3b7c, 0x0cd5: 0x3b7f, 0x0cd6: 0x3b82, 0x0cd7: 0x056c, + 0x0cd8: 0x0979, 0x0cd9: 0x3b85, 0x0cda: 0x3b88, 0x0cdb: 0x3b8b, 0x0cdc: 0x057e, 0x0cdd: 0x055c, + 0x0cde: 0x0572, 0x0cdf: 0x056c, 0x0ce0: 0x0575, 0x0ce1: 0x056f, 0x0ce2: 0x3b2d, 0x0ce3: 0x3b30, + 0x0ce4: 0x14eb, 0x0ce5: 0x3b33, 0x0ce6: 0x3b36, 0x0ce7: 0x3b39, 0x0ce8: 0x3b3c, 0x0ce9: 0x057b, + 0x0cea: 0x3b3f, 0x0ceb: 0x3b42, 0x0cec: 0x3b45, 0x0ced: 0x3b48, 0x0cee: 0x3b4b, 0x0cef: 0x3b4e, + 0x0cf0: 0x3b51, 0x0cf1: 0x14ee, 0x0cf2: 0x3b54, 0x0cf3: 0x057b, 0x0cf4: 0x0581, 0x0cf5: 0x3b57, + 0x0cf6: 0x055f, 0x0cf7: 0x3b5a, 0x0cf8: 0x3b5d, 0x0cf9: 0x3b60, 0x0cfa: 0x14d6, 0x0cfb: 0x3b63, + 0x0cfc: 0x3b67, 0x0cfd: 0x0559, 0x0cfe: 0x0973, 0x0cff: 0x0976, // Block 0x34, offset 0xd00 - 0x0d00: 0x0fff, 0x0d01: 0x1004, 0x0d02: 0x1009, 0x0d03: 0x1010, 0x0d04: 0x1017, 0x0d05: 0x101e, - 0x0d08: 0x1025, 0x0d09: 0x102a, 0x0d0a: 0x102f, 0x0d0b: 0x1036, - 0x0d0c: 0x103d, 0x0d0d: 0x1044, 0x0d10: 0x104b, 0x0d11: 0x1050, - 0x0d12: 0x1055, 0x0d13: 0x105c, 0x0d14: 0x1063, 0x0d15: 0x106a, 0x0d16: 0x1071, 0x0d17: 0x1078, - 0x0d19: 0x107f, 0x0d1b: 0x1084, 0x0d1d: 0x108b, - 0x0d1f: 0x1092, 0x0d20: 0x1099, 0x0d21: 0x109e, 0x0d22: 0x10a3, 0x0d23: 0x10aa, - 0x0d24: 0x10b1, 0x0d25: 0x10b8, 0x0d26: 0x10bf, 0x0d27: 0x10c6, 0x0d28: 0x10cd, 0x0d29: 0x10d2, - 0x0d2a: 0x10d7, 0x0d2b: 0x10de, 0x0d2c: 0x10e5, 0x0d2d: 0x10ec, 0x0d2e: 0x10f3, 0x0d2f: 0x10fa, - 0x0d30: 0x1101, 0x0d31: 0x0525, 0x0d32: 0x1106, 0x0d33: 0x052a, 0x0d34: 0x110b, 0x0d35: 0x052f, - 0x0d36: 0x1110, 0x0d37: 0x0534, 0x0d38: 0x1115, 0x0d39: 0x054a, 0x0d3a: 0x111a, 0x0d3b: 0x054f, - 0x0d3c: 0x111f, 0x0d3d: 0x0554, + 0x0d00: 0x057e, 0x0d01: 0x3b6a, 0x0d02: 0x3b6d, 0x0d03: 0x055c, 0x0d04: 0x12fd, 0x0d05: 0x0572, + 0x0d06: 0x3b70, 0x0d07: 0x0015, 0x0d08: 0x3b73, 0x0d09: 0x3b76, 0x0d0a: 0x3b79, 0x0d0b: 0x056f, + 0x0d0c: 0x0575, 0x0d0d: 0x0578, 0x0d0e: 0x3b7c, 0x0d0f: 0x3b7f, 0x0d10: 0x3b82, 0x0d11: 0x056c, + 0x0d12: 0x0979, 0x0d13: 0x3b85, 0x0d14: 0x3b88, 0x0d15: 0x3b8b, 0x0d16: 0x057e, 0x0d17: 0x055c, + 0x0d18: 0x0572, 0x0d19: 0x056c, 0x0d1a: 0x0575, 0x0d1b: 0x056f, 0x0d1c: 0x3b2d, 0x0d1d: 0x3b30, + 0x0d1e: 0x14eb, 0x0d1f: 0x3b33, 0x0d20: 0x3b36, 0x0d21: 0x3b39, 0x0d22: 0x3b3c, 0x0d23: 0x057b, + 0x0d24: 0x3b3f, 0x0d25: 0x3b42, 0x0d26: 0x3b45, 0x0d27: 0x3b48, 0x0d28: 0x3b4b, 0x0d29: 0x3b4e, + 0x0d2a: 0x3b51, 0x0d2b: 0x14ee, 0x0d2c: 0x3b54, 0x0d2d: 0x057b, 0x0d2e: 0x0581, 0x0d2f: 0x3b57, + 0x0d30: 0x055f, 0x0d31: 0x3b5a, 0x0d32: 0x3b5d, 0x0d33: 0x3b60, 0x0d34: 0x14d6, 0x0d35: 0x3b63, + 0x0d36: 0x3b67, 0x0d37: 0x0559, 0x0d38: 0x0973, 0x0d39: 0x0976, 0x0d3a: 0x057e, 0x0d3b: 0x3b6a, + 0x0d3c: 0x3b6d, 0x0d3d: 0x055c, 0x0d3e: 0x12fd, 0x0d3f: 0x0572, // Block 0x35, offset 0xd40 - 0x0d40: 0x1124, 0x0d41: 0x112b, 0x0d42: 0x1132, 0x0d43: 0x113b, 0x0d44: 0x1144, 0x0d45: 0x114d, - 0x0d46: 0x1156, 0x0d47: 0x115f, 0x0d48: 0x1168, 0x0d49: 0x116f, 0x0d4a: 0x1176, 0x0d4b: 0x117f, - 0x0d4c: 0x1188, 0x0d4d: 0x1191, 0x0d4e: 0x119a, 0x0d4f: 0x11a3, 0x0d50: 0x11ac, 0x0d51: 0x11b3, - 0x0d52: 0x11ba, 0x0d53: 0x11c3, 0x0d54: 0x11cc, 0x0d55: 0x11d5, 0x0d56: 0x11de, 0x0d57: 0x11e7, - 0x0d58: 0x11f0, 0x0d59: 0x11f7, 0x0d5a: 0x11fe, 0x0d5b: 0x1207, 0x0d5c: 0x1210, 0x0d5d: 0x1219, - 0x0d5e: 0x1222, 0x0d5f: 0x122b, 0x0d60: 0x1234, 0x0d61: 0x123b, 0x0d62: 0x1242, 0x0d63: 0x124b, - 0x0d64: 0x1254, 0x0d65: 0x125d, 0x0d66: 0x1266, 0x0d67: 0x126f, 0x0d68: 0x1278, 0x0d69: 0x127f, - 0x0d6a: 0x1286, 0x0d6b: 0x128f, 0x0d6c: 0x1298, 0x0d6d: 0x12a1, 0x0d6e: 0x12aa, 0x0d6f: 0x12b3, - 0x0d70: 0x12bc, 0x0d71: 0x12c1, 0x0d72: 0x12c6, 0x0d73: 0x12cd, 0x0d74: 0x12d2, - 0x0d76: 0x12d9, 0x0d77: 0x12de, 0x0d78: 0x12e5, 0x0d79: 0x12ea, 0x0d7a: 0x12ef, 0x0d7b: 0x04ee, - 0x0d7c: 0x12f4, 0x0d7d: 0x12f9, 0x0d7e: 0x12fd, 0x0d7f: 0x12f9, + 0x0d40: 0x3b70, 0x0d41: 0x0015, 0x0d42: 0x3b73, 0x0d43: 0x3b76, 0x0d44: 0x3b79, 0x0d45: 0x056f, + 0x0d46: 0x0575, 0x0d47: 0x0578, 0x0d48: 0x3b7c, 0x0d49: 0x3b7f, 0x0d4a: 0x3b82, 0x0d4b: 0x056c, + 0x0d4c: 0x0979, 0x0d4d: 0x3b85, 0x0d4e: 0x3b88, 0x0d4f: 0x3b8b, 0x0d50: 0x057e, 0x0d51: 0x055c, + 0x0d52: 0x0572, 0x0d53: 0x056c, 0x0d54: 0x0575, 0x0d55: 0x056f, 0x0d56: 0x3b2d, 0x0d57: 0x3b30, + 0x0d58: 0x14eb, 0x0d59: 0x3b33, 0x0d5a: 0x3b36, 0x0d5b: 0x3b39, 0x0d5c: 0x3b3c, 0x0d5d: 0x057b, + 0x0d5e: 0x3b3f, 0x0d5f: 0x3b42, 0x0d60: 0x3b45, 0x0d61: 0x3b48, 0x0d62: 0x3b4b, 0x0d63: 0x3b4e, + 0x0d64: 0x3b51, 0x0d65: 0x14ee, 0x0d66: 0x3b54, 0x0d67: 0x057b, 0x0d68: 0x0581, 0x0d69: 0x3b57, + 0x0d6a: 0x055f, 0x0d6b: 0x3b5a, 0x0d6c: 0x3b5d, 0x0d6d: 0x3b60, 0x0d6e: 0x14d6, 0x0d6f: 0x3b63, + 0x0d70: 0x3b67, 0x0d71: 0x0559, 0x0d72: 0x0973, 0x0d73: 0x0976, 0x0d74: 0x057e, 0x0d75: 0x3b6a, + 0x0d76: 0x3b6d, 0x0d77: 0x055c, 0x0d78: 0x12fd, 0x0d79: 0x0572, 0x0d7a: 0x3b70, 0x0d7b: 0x0015, + 0x0d7c: 0x3b73, 0x0d7d: 0x3b76, 0x0d7e: 0x3b79, 0x0d7f: 0x056f, // Block 0x36, offset 0xd80 - 0x0d80: 0x1300, 0x0d81: 0x1309, 0x0d82: 0x130f, 0x0d83: 0x1316, 0x0d84: 0x131b, - 0x0d86: 0x1322, 0x0d87: 0x1327, 0x0d88: 0x132e, 0x0d89: 0x04f6, 0x0d8a: 0x1333, 0x0d8b: 0x04fb, - 0x0d8c: 0x1338, 0x0d8d: 0x1343, 0x0d8e: 0x134f, 0x0d8f: 0x135b, 0x0d90: 0x1361, 0x0d91: 0x1366, - 0x0d92: 0x136b, 0x0d93: 0x0514, 0x0d96: 0x1372, 0x0d97: 0x1377, - 0x0d98: 0x137e, 0x0d99: 0x1383, 0x0d9a: 0x1388, 0x0d9b: 0x0500, 0x0d9d: 0x1393, - 0x0d9e: 0x139f, 0x0d9f: 0x13ab, 0x0da0: 0x13b1, 0x0da1: 0x13b6, 0x0da2: 0x13bb, 0x0da3: 0x0539, - 0x0da4: 0x13c2, 0x0da5: 0x13c7, 0x0da6: 0x13cc, 0x0da7: 0x13d1, 0x0da8: 0x13d8, 0x0da9: 0x13dd, - 0x0daa: 0x13e2, 0x0dab: 0x050a, 0x0dac: 0x13e7, 0x0dad: 0x13f1, 0x0dae: 0x04e8, 0x0daf: 0x13f7, - 0x0db2: 0x13f9, 0x0db3: 0x1400, 0x0db4: 0x1405, - 0x0db6: 0x140c, 0x0db7: 0x1411, 0x0db8: 0x1418, 0x0db9: 0x0505, 0x0dba: 0x141d, 0x0dbb: 0x050f, - 0x0dbc: 0x1422, 0x0dbd: 0x0011, 0x0dbe: 0x142a, + 0x0d80: 0x0575, 0x0d81: 0x0578, 0x0d82: 0x3b7c, 0x0d83: 0x3b7f, 0x0d84: 0x3b82, 0x0d85: 0x056c, + 0x0d86: 0x0979, 0x0d87: 0x3b85, 0x0d88: 0x3b88, 0x0d89: 0x3b8b, 0x0d8a: 0x057e, 0x0d8b: 0x055c, + 0x0d8c: 0x0572, 0x0d8d: 0x056c, 0x0d8e: 0x0575, 0x0d8f: 0x056f, 0x0d90: 0x3b2d, 0x0d91: 0x3b30, + 0x0d92: 0x14eb, 0x0d93: 0x3b33, 0x0d94: 0x3b36, 0x0d95: 0x3b39, 0x0d96: 0x3b3c, 0x0d97: 0x057b, + 0x0d98: 0x3b3f, 0x0d99: 0x3b42, 0x0d9a: 0x3b45, 0x0d9b: 0x3b48, 0x0d9c: 0x3b4b, 0x0d9d: 0x3b4e, + 0x0d9e: 0x3b51, 0x0d9f: 0x14ee, 0x0da0: 0x3b54, 0x0da1: 0x057b, 0x0da2: 0x0581, 0x0da3: 0x3b57, + 0x0da4: 0x055f, 0x0da5: 0x3b5a, 0x0da6: 0x3b5d, 0x0da7: 0x3b60, 0x0da8: 0x14d6, 0x0da9: 0x3b63, + 0x0daa: 0x3b67, 0x0dab: 0x0559, 0x0dac: 0x0973, 0x0dad: 0x0976, 0x0dae: 0x057e, 0x0daf: 0x3b6a, + 0x0db0: 0x3b6d, 0x0db1: 0x055c, 0x0db2: 0x12fd, 0x0db3: 0x0572, 0x0db4: 0x3b70, 0x0db5: 0x0015, + 0x0db6: 0x3b73, 0x0db7: 0x3b76, 0x0db8: 0x3b79, 0x0db9: 0x056f, 0x0dba: 0x0575, 0x0dbb: 0x0578, + 0x0dbc: 0x3b7c, 0x0dbd: 0x3b7f, 0x0dbe: 0x3b82, 0x0dbf: 0x056c, // Block 0x37, offset 0xdc0 - 0x0dc0: 0x0001, 0x0dc1: 0x0001, 0x0dc2: 0x0001, 0x0dc3: 0x0001, 0x0dc4: 0x0001, 0x0dc5: 0x0001, - 0x0dc6: 0x0001, 0x0dc7: 0x0001, 0x0dc8: 0x0001, 0x0dc9: 0x0001, 0x0dca: 0x0001, - 0x0dd1: 0x1436, - 0x0dd7: 0x143a, - 0x0de4: 0x143e, 0x0de5: 0x1440, 0x0de6: 0x1443, - 0x0def: 0x0001, - 0x0df3: 0x1447, 0x0df4: 0x144e, - 0x0df6: 0x1458, 0x0df7: 0x145f, - 0x0dfc: 0x1469, 0x0dfe: 0x146c, + 0x0dc0: 0x0979, 0x0dc1: 0x3b85, 0x0dc2: 0x3b88, 0x0dc3: 0x3b8b, 0x0dc4: 0x057e, 0x0dc5: 0x055c, + 0x0dc6: 0x0572, 0x0dc7: 0x056c, 0x0dc8: 0x0575, 0x0dc9: 0x056f, 0x0dca: 0x3b8f, 0x0dcb: 0x3b92, + 0x0dce: 0x1486, 0x0dcf: 0x001c, 0x0dd0: 0x000d, 0x0dd1: 0x000f, + 0x0dd2: 0x1488, 0x0dd3: 0x148a, 0x0dd4: 0x148c, 0x0dd5: 0x148e, 0x0dd6: 0x1490, 0x0dd7: 0x1492, + 0x0dd8: 0x1486, 0x0dd9: 0x001c, 0x0dda: 0x000d, 0x0ddb: 0x000f, 0x0ddc: 0x1488, 0x0ddd: 0x148a, + 0x0dde: 0x148c, 0x0ddf: 0x148e, 0x0de0: 0x1490, 0x0de1: 0x1492, 0x0de2: 0x1486, 0x0de3: 0x001c, + 0x0de4: 0x000d, 0x0de5: 0x000f, 0x0de6: 0x1488, 0x0de7: 0x148a, 0x0de8: 0x148c, 0x0de9: 0x148e, + 0x0dea: 0x1490, 0x0deb: 0x1492, 0x0dec: 0x1486, 0x0ded: 0x001c, 0x0dee: 0x000d, 0x0def: 0x000f, + 0x0df0: 0x1488, 0x0df1: 0x148a, 0x0df2: 0x148c, 0x0df3: 0x148e, 0x0df4: 0x1490, 0x0df5: 0x1492, + 0x0df6: 0x1486, 0x0df7: 0x001c, 0x0df8: 0x000d, 0x0df9: 0x000f, 0x0dfa: 0x1488, 0x0dfb: 0x148a, + 0x0dfc: 0x148c, 0x0dfd: 0x148e, 0x0dfe: 0x1490, 0x0dff: 0x1492, // Block 0x38, offset 0xe00 - 0x0e07: 0x1470, 0x0e08: 0x1473, 0x0e09: 0x1476, - 0x0e17: 0x1479, - 0x0e1f: 0x0001, - 0x0e30: 0x1486, 0x0e31: 0x097c, 0x0e34: 0x1488, 0x0e35: 0x148a, - 0x0e36: 0x148c, 0x0e37: 0x148e, 0x0e38: 0x1490, 0x0e39: 0x1492, 0x0e3a: 0x1494, 0x0e3b: 0x1496, - 0x0e3c: 0x149a, 0x0e3d: 0x149c, 0x0e3e: 0x149e, 0x0e3f: 0x14a0, + 0x0e00: 0x3b95, 0x0e01: 0x3b98, 0x0e02: 0x3b9b, 0x0e03: 0x3b9e, 0x0e04: 0x3ba1, 0x0e05: 0x3ba4, + 0x0e06: 0x3ba7, 0x0e07: 0x3baa, 0x0e08: 0x3bad, 0x0e09: 0x3bb0, 0x0e0a: 0x3bb3, + 0x0e10: 0x3bb6, 0x0e11: 0x3bba, + 0x0e12: 0x3bbe, 0x0e13: 0x3bc2, 0x0e14: 0x3bc6, 0x0e15: 0x3bca, 0x0e16: 0x3bce, 0x0e17: 0x3bd2, + 0x0e18: 0x3bd6, 0x0e19: 0x3bda, 0x0e1a: 0x3bde, 0x0e1b: 0x3be2, 0x0e1c: 0x3be6, 0x0e1d: 0x3bea, + 0x0e1e: 0x3bee, 0x0e1f: 0x3bf2, 0x0e20: 0x3bf6, 0x0e21: 0x3bfa, 0x0e22: 0x3bfe, 0x0e23: 0x3c02, + 0x0e24: 0x3c06, 0x0e25: 0x3c0a, 0x0e26: 0x3c0e, 0x0e27: 0x3c12, 0x0e28: 0x3c16, 0x0e29: 0x3c1a, + 0x0e2a: 0x3c1e, 0x0e2b: 0x14ad, 0x0e2c: 0x092b, 0x0e2d: 0x3c26, 0x0e2e: 0x3c29, + 0x0e30: 0x0906, 0x0e31: 0x090b, 0x0e32: 0x14ad, 0x0e33: 0x090d, 0x0e34: 0x090f, 0x0e35: 0x14d9, + 0x0e36: 0x0914, 0x0e37: 0x0916, 0x0e38: 0x0918, 0x0e39: 0x091a, 0x0e3a: 0x091c, 0x0e3b: 0x091e, + 0x0e3c: 0x0920, 0x0e3d: 0x0922, 0x0e3e: 0x0924, 0x0e3f: 0x0929, // Block 0x39, offset 0xe40 - 0x0e40: 0x1486, 0x0e41: 0x001c, 0x0e42: 0x000d, 0x0e43: 0x000f, 0x0e44: 0x1488, 0x0e45: 0x148a, - 0x0e46: 0x148c, 0x0e47: 0x148e, 0x0e48: 0x1490, 0x0e49: 0x1492, 0x0e4a: 0x1494, 0x0e4b: 0x1496, - 0x0e4c: 0x149a, 0x0e4d: 0x149c, 0x0e4e: 0x149e, 0x0e50: 0x0007, 0x0e51: 0x0941, - 0x0e52: 0x001e, 0x0e53: 0x04c7, 0x0e54: 0x0943, 0x0e55: 0x0494, 0x0e56: 0x094e, 0x0e57: 0x04c5, - 0x0e58: 0x0950, 0x0e59: 0x14a0, 0x0e5a: 0x0960, 0x0e5b: 0x02c8, 0x0e5c: 0x0962, - 0x0e68: 0x14a2, + 0x0e40: 0x3c3f, 0x0e41: 0x3c46, 0x0e42: 0x2291, + 0x0e50: 0x1922, 0x0e51: 0x3c4d, + 0x0e52: 0x3c51, 0x0e53: 0x1cb3, 0x0e54: 0x183e, 0x0e55: 0x3c55, 0x0e56: 0x3c59, 0x0e57: 0x1ed0, + 0x0e58: 0x3c5d, 0x0e59: 0x3c61, 0x0e5a: 0x3c65, 0x0e5b: 0x2d49, 0x0e5c: 0x3c69, 0x0e5d: 0x3c6d, + 0x0e5e: 0x3c71, 0x0e5f: 0x3c75, 0x0e60: 0x3c79, 0x0e61: 0x3c7d, 0x0e62: 0x19b2, 0x0e63: 0x3c81, + 0x0e64: 0x3c85, 0x0e65: 0x3c89, 0x0e66: 0x3c8d, 0x0e67: 0x3c91, 0x0e68: 0x3c95, 0x0e69: 0x1826, + 0x0e6a: 0x1eb0, 0x0e6b: 0x3c99, 0x0e6c: 0x21c7, 0x0e6d: 0x1ebc, 0x0e6e: 0x21cb, 0x0e6f: 0x3c9d, + 0x0e70: 0x1a92, 0x0e71: 0x3ca1, 0x0e72: 0x3ca5, 0x0e73: 0x3ca9, 0x0e74: 0x3cad, 0x0e75: 0x3cb1, + 0x0e76: 0x2183, 0x0e77: 0x194a, 0x0e78: 0x3cb5, 0x0e79: 0x3cb9, 0x0e7a: 0x3cbd, // Block 0x3a, offset 0xe80 - 0x0e80: 0x14a5, 0x0e81: 0x14a9, 0x0e82: 0x14ad, 0x0e83: 0x14af, 0x0e85: 0x14b3, - 0x0e86: 0x14b7, 0x0e87: 0x14bb, 0x0e89: 0x14be, 0x0e8a: 0x094c, 0x0e8b: 0x0916, - 0x0e8c: 0x0916, 0x0e8d: 0x0916, 0x0e8e: 0x0494, 0x0e8f: 0x14c2, 0x0e90: 0x0918, 0x0e91: 0x0918, - 0x0e92: 0x091e, 0x0e93: 0x04c5, 0x0e95: 0x0922, 0x0e96: 0x14c5, - 0x0e99: 0x0929, 0x0e9a: 0x14c8, 0x0e9b: 0x092b, 0x0e9c: 0x092b, 0x0e9d: 0x092b, - 0x0ea0: 0x14ca, 0x0ea1: 0x14cd, 0x0ea2: 0x14d1, - 0x0ea4: 0x14d4, 0x0ea6: 0x14d6, 0x0ea8: 0x14d4, - 0x0eaa: 0x091c, 0x0eab: 0x0046, 0x0eac: 0x090b, 0x0ead: 0x14ad, 0x0eaf: 0x0941, - 0x0eb0: 0x090f, 0x0eb1: 0x14d9, 0x0eb3: 0x0920, 0x0eb4: 0x001e, 0x0eb5: 0x14db, - 0x0eb6: 0x14de, 0x0eb7: 0x14e1, 0x0eb8: 0x14e4, 0x0eb9: 0x097c, 0x0ebb: 0x14e7, - 0x0ebc: 0x056f, 0x0ebd: 0x0973, 0x0ebe: 0x14eb, 0x0ebf: 0x14ee, + 0x0e80: 0x3d23, 0x0e81: 0x3d27, 0x0e82: 0x3d2b, 0x0e83: 0x3d2f, 0x0e84: 0x3d34, 0x0e85: 0x2eb5, + 0x0e86: 0x3d38, 0x0e87: 0x3d3c, 0x0e88: 0x3d40, 0x0e89: 0x3d44, 0x0e8a: 0x2eb9, 0x0e8b: 0x3d48, + 0x0e8c: 0x3d4c, 0x0e8d: 0x3d50, 0x0e8e: 0x2ebd, 0x0e8f: 0x3d55, 0x0e90: 0x3d59, 0x0e91: 0x3d5d, + 0x0e92: 0x3d61, 0x0e93: 0x3d66, 0x0e94: 0x3d6a, 0x0e95: 0x3c71, 0x0e96: 0x3d6e, 0x0e97: 0x3d73, + 0x0e98: 0x3d77, 0x0e99: 0x3d7b, 0x0e9a: 0x3d7f, 0x0e9b: 0x2f9a, 0x0e9c: 0x3d83, 0x0e9d: 0x1866, + 0x0e9e: 0x3d88, 0x0e9f: 0x3d8c, 0x0ea0: 0x3d90, 0x0ea1: 0x3d94, 0x0ea2: 0x3cb9, 0x0ea3: 0x3d98, + 0x0ea4: 0x3d9c, 0x0ea5: 0x2fae, 0x0ea6: 0x2ec1, 0x0ea7: 0x2ec5, 0x0ea8: 0x2fb2, 0x0ea9: 0x3da0, + 0x0eaa: 0x3da4, 0x0eab: 0x2bf1, 0x0eac: 0x3da8, 0x0ead: 0x2ec9, 0x0eae: 0x3dac, 0x0eaf: 0x3db0, + 0x0eb0: 0x3db4, 0x0eb1: 0x3db8, 0x0eb2: 0x3db8, 0x0eb3: 0x3db8, 0x0eb4: 0x3dbc, 0x0eb5: 0x3dc1, + 0x0eb6: 0x3dc5, 0x0eb7: 0x3dc9, 0x0eb8: 0x3dcd, 0x0eb9: 0x3dd2, 0x0eba: 0x3dd6, 0x0ebb: 0x3dda, + 0x0ebc: 0x3dde, 0x0ebd: 0x3de2, 0x0ebe: 0x3de6, 0x0ebf: 0x3dea, // Block 0x3b, offset 0xec0 - 0x0ec0: 0x14f1, 0x0ec5: 0x090d, - 0x0ec6: 0x093f, 0x0ec7: 0x0941, 0x0ec8: 0x097c, 0x0ec9: 0x0499, - 0x0ed0: 0x14f5, 0x0ed1: 0x14fb, - 0x0ed2: 0x1501, 0x0ed3: 0x1508, 0x0ed4: 0x150e, 0x0ed5: 0x1514, 0x0ed6: 0x151a, 0x0ed7: 0x1520, - 0x0ed8: 0x1526, 0x0ed9: 0x152c, 0x0eda: 0x1532, 0x0edb: 0x1538, 0x0edc: 0x153e, 0x0edd: 0x1544, - 0x0ede: 0x154a, 0x0edf: 0x1550, 0x0ee0: 0x0918, 0x0ee1: 0x1555, 0x0ee2: 0x1558, 0x0ee3: 0x155c, - 0x0ee4: 0x155f, 0x0ee5: 0x1561, 0x0ee6: 0x1564, 0x0ee7: 0x1568, 0x0ee8: 0x156d, 0x0ee9: 0x1570, - 0x0eea: 0x1572, 0x0eeb: 0x1575, 0x0eec: 0x091e, 0x0eed: 0x14ad, 0x0eee: 0x090d, 0x0eef: 0x0920, - 0x0ef0: 0x097c, 0x0ef1: 0x1579, 0x0ef2: 0x157c, 0x0ef3: 0x1580, 0x0ef4: 0x096d, 0x0ef5: 0x1583, - 0x0ef6: 0x1586, 0x0ef7: 0x158a, 0x0ef8: 0x158f, 0x0ef9: 0x04c7, 0x0efa: 0x1592, 0x0efb: 0x1595, - 0x0efc: 0x04c5, 0x0efd: 0x0984, 0x0efe: 0x093f, 0x0eff: 0x0950, + 0x0ec0: 0x3dee, 0x0ec1: 0x3df2, 0x0ec2: 0x3df6, 0x0ec3: 0x3dfa, 0x0ec4: 0x3dfe, 0x0ec5: 0x3e02, + 0x0ec6: 0x3e02, 0x0ec7: 0x2fba, 0x0ec8: 0x3e06, 0x0ec9: 0x3e0a, 0x0eca: 0x3e0e, 0x0ecb: 0x3e12, + 0x0ecc: 0x2ed1, 0x0ecd: 0x3e16, 0x0ece: 0x3e1a, 0x0ecf: 0x3e1e, 0x0ed0: 0x2e39, 0x0ed1: 0x3e22, + 0x0ed2: 0x3e26, 0x0ed3: 0x3e2a, 0x0ed4: 0x3e2e, 0x0ed5: 0x3e32, 0x0ed6: 0x3e36, 0x0ed7: 0x3e3a, + 0x0ed8: 0x3e3e, 0x0ed9: 0x3e42, 0x0eda: 0x3e47, 0x0edb: 0x3e4b, 0x0edc: 0x3e4f, 0x0edd: 0x3c55, + 0x0ede: 0x3e53, 0x0edf: 0x3e57, 0x0ee0: 0x3e5b, 0x0ee1: 0x3e60, 0x0ee2: 0x3e65, 0x0ee3: 0x3e69, + 0x0ee4: 0x3e6d, 0x0ee5: 0x3e71, 0x0ee6: 0x3e75, 0x0ee7: 0x3e79, 0x0ee8: 0x3e7d, 0x0ee9: 0x3e81, + 0x0eea: 0x3e85, 0x0eeb: 0x3e85, 0x0eec: 0x3e89, 0x0eed: 0x3e8e, 0x0eee: 0x3e92, 0x0eef: 0x2be1, + 0x0ef0: 0x3e96, 0x0ef1: 0x3e9a, 0x0ef2: 0x3e9f, 0x0ef3: 0x3ea3, 0x0ef4: 0x3ea7, 0x0ef5: 0x18ce, + 0x0ef6: 0x3eab, 0x0ef7: 0x3eaf, 0x0ef8: 0x18d6, 0x0ef9: 0x3eb3, 0x0efa: 0x3eb7, 0x0efb: 0x3ebb, + 0x0efc: 0x3ec0, 0x0efd: 0x3ec4, 0x0efe: 0x3ec9, 0x0eff: 0x3ecd, // Block 0x3c, offset 0xf00 - 0x0f09: 0x1599, - 0x0f1a: 0x159f, 0x0f1b: 0x15a5, - 0x0f2e: 0x15ab, + 0x0f00: 0x3ed1, 0x0f01: 0x3ed5, 0x0f02: 0x3ed9, 0x0f03: 0x3edd, 0x0f04: 0x3ee1, 0x0f05: 0x3ee5, + 0x0f06: 0x3ee9, 0x0f07: 0x3eed, 0x0f08: 0x3ef1, 0x0f09: 0x3ef5, 0x0f0a: 0x3efa, 0x0f0b: 0x3efe, + 0x0f0c: 0x3f02, 0x0f0d: 0x3f06, 0x0f0e: 0x2b11, 0x0f0f: 0x3f0a, 0x0f10: 0x18fe, 0x0f11: 0x3f0f, + 0x0f12: 0x3f0f, 0x0f13: 0x3f14, 0x0f14: 0x3f18, 0x0f15: 0x3f18, 0x0f16: 0x3f1c, 0x0f17: 0x3f20, + 0x0f18: 0x3f25, 0x0f19: 0x3f2a, 0x0f1a: 0x3f2e, 0x0f1b: 0x3f32, 0x0f1c: 0x3f36, 0x0f1d: 0x3f3a, + 0x0f1e: 0x3f3e, 0x0f1f: 0x3f42, 0x0f20: 0x3f46, 0x0f21: 0x3f4a, 0x0f22: 0x3f4e, 0x0f23: 0x2ee5, + 0x0f24: 0x3f52, 0x0f25: 0x3f57, 0x0f26: 0x3f5b, 0x0f27: 0x3f5f, 0x0f28: 0x2fea, 0x0f29: 0x3f5f, + 0x0f2a: 0x3f63, 0x0f2b: 0x2eed, 0x0f2c: 0x3f67, 0x0f2d: 0x3f6b, 0x0f2e: 0x3f6f, 0x0f2f: 0x3f73, + 0x0f30: 0x2ef1, 0x0f31: 0x2aa5, 0x0f32: 0x3f77, 0x0f33: 0x3f7b, 0x0f34: 0x3f7f, 0x0f35: 0x3f83, + 0x0f36: 0x3f87, 0x0f37: 0x3f8b, 0x0f38: 0x3f8f, 0x0f39: 0x3f94, 0x0f3a: 0x3f98, 0x0f3b: 0x3f9c, + 0x0f3c: 0x3fa0, 0x0f3d: 0x3fa4, 0x0f3e: 0x3fa8, 0x0f3f: 0x3fad, // Block 0x3d, offset 0xf40 - 0x0f4d: 0x15b1, 0x0f4e: 0x15b7, 0x0f4f: 0x15bd, + 0x0f40: 0x3fb1, 0x0f41: 0x3fb5, 0x0f42: 0x3fb9, 0x0f43: 0x3fbd, 0x0f44: 0x3fc1, 0x0f45: 0x3fc5, + 0x0f46: 0x3fc9, 0x0f47: 0x3fcd, 0x0f48: 0x2ef5, 0x0f49: 0x3fd1, 0x0f4a: 0x3fd5, 0x0f4b: 0x3fda, + 0x0f4c: 0x3fde, 0x0f4d: 0x3fe2, 0x0f4e: 0x3fe6, 0x0f4f: 0x2efd, 0x0f50: 0x3fea, 0x0f51: 0x3fee, + 0x0f52: 0x3ff2, 0x0f53: 0x3ff6, 0x0f54: 0x3ffa, 0x0f55: 0x3ffe, 0x0f56: 0x4002, 0x0f57: 0x4006, + 0x0f58: 0x2b15, 0x0f59: 0x300a, 0x0f5a: 0x400a, 0x0f5b: 0x400e, 0x0f5c: 0x4012, 0x0f5d: 0x4016, + 0x0f5e: 0x401b, 0x0f5f: 0x401f, 0x0f60: 0x4023, 0x0f61: 0x4027, 0x0f62: 0x2f01, 0x0f63: 0x402b, + 0x0f64: 0x4030, 0x0f65: 0x4034, 0x0f66: 0x4038, 0x0f67: 0x30b5, 0x0f68: 0x403c, 0x0f69: 0x4040, + 0x0f6a: 0x4044, 0x0f6b: 0x4048, 0x0f6c: 0x404c, 0x0f6d: 0x4051, 0x0f6e: 0x4055, 0x0f6f: 0x4059, + 0x0f70: 0x405d, 0x0f71: 0x4062, 0x0f72: 0x4066, 0x0f73: 0x406a, 0x0f74: 0x406e, 0x0f75: 0x2c25, + 0x0f76: 0x4072, 0x0f77: 0x4076, 0x0f78: 0x407b, 0x0f79: 0x4080, 0x0f7a: 0x4085, 0x0f7b: 0x4089, + 0x0f7c: 0x408e, 0x0f7d: 0x4092, 0x0f7e: 0x4096, 0x0f7f: 0x409a, // Block 0x3e, offset 0xf80 - 0x0f84: 0x15c3, - 0x0f89: 0x15c9, - 0x0f8c: 0x15cf, - 0x0fa4: 0x15d5, 0x0fa6: 0x15db, - 0x0fac: 0x15e1, 0x0fad: 0x15e8, 0x0faf: 0x15f2, - 0x0fb0: 0x15f9, + 0x0f80: 0x409e, 0x0f81: 0x2f05, 0x0f82: 0x2d71, 0x0f83: 0x40a2, 0x0f84: 0x40a6, 0x0f85: 0x40aa, + 0x0f86: 0x40ae, 0x0f87: 0x40b3, 0x0f88: 0x40b7, 0x0f89: 0x40bb, 0x0f8a: 0x40bf, 0x0f8b: 0x3016, + 0x0f8c: 0x40c3, 0x0f8d: 0x40c7, 0x0f8e: 0x40cc, 0x0f8f: 0x40d0, 0x0f90: 0x40d4, 0x0f91: 0x40d9, + 0x0f92: 0x40de, 0x0f93: 0x40e2, 0x0f94: 0x301a, 0x0f95: 0x40e6, 0x0f96: 0x40ea, 0x0f97: 0x40ee, + 0x0f98: 0x40f2, 0x0f99: 0x40f6, 0x0f9a: 0x40fa, 0x0f9b: 0x40fe, 0x0f9c: 0x4103, 0x0f9d: 0x4107, + 0x0f9e: 0x410c, 0x0f9f: 0x4110, 0x0fa0: 0x4115, 0x0fa1: 0x3022, 0x0fa2: 0x4119, 0x0fa3: 0x411d, + 0x0fa4: 0x4122, 0x0fa5: 0x4126, 0x0fa6: 0x412a, 0x0fa7: 0x412f, 0x0fa8: 0x4134, 0x0fa9: 0x4138, + 0x0faa: 0x413c, 0x0fab: 0x4140, 0x0fac: 0x4144, 0x0fad: 0x4144, 0x0fae: 0x4148, 0x0faf: 0x414c, + 0x0fb0: 0x302a, 0x0fb1: 0x4150, 0x0fb2: 0x4154, 0x0fb3: 0x4158, 0x0fb4: 0x415c, 0x0fb5: 0x4160, + 0x0fb6: 0x4165, 0x0fb7: 0x4169, 0x0fb8: 0x2bed, 0x0fb9: 0x416e, 0x0fba: 0x4173, 0x0fbb: 0x4177, + 0x0fbc: 0x417c, 0x0fbd: 0x4181, 0x0fbe: 0x4186, 0x0fbf: 0x418a, // Block 0x3f, offset 0xfc0 - 0x0fc1: 0x1603, 0x0fc4: 0x1609, - 0x0fc7: 0x160f, 0x0fc9: 0x1615, - 0x0fe0: 0x161b, 0x0fe2: 0x161f, - 0x0fed: 0x1625, 0x0fee: 0x162b, 0x0fef: 0x162f, - 0x0ff0: 0x1633, 0x0ff1: 0x1639, 0x0ff4: 0x163f, 0x0ff5: 0x1645, - 0x0ff8: 0x164b, 0x0ff9: 0x1651, + 0x0fc0: 0x3042, 0x0fc1: 0x418e, 0x0fc2: 0x4193, 0x0fc3: 0x4198, 0x0fc4: 0x419d, 0x0fc5: 0x41a2, + 0x0fc6: 0x41a6, 0x0fc7: 0x41a6, 0x0fc8: 0x3046, 0x0fc9: 0x30bd, 0x0fca: 0x41aa, 0x0fcb: 0x41ae, + 0x0fcc: 0x41b2, 0x0fcd: 0x41b6, 0x0fce: 0x41bb, 0x0fcf: 0x2b59, 0x0fd0: 0x304e, 0x0fd1: 0x41bf, + 0x0fd2: 0x41c3, 0x0fd3: 0x2f2d, 0x0fd4: 0x41c8, 0x0fd5: 0x41cd, 0x0fd6: 0x2e89, 0x0fd7: 0x41d2, + 0x0fd8: 0x41d6, 0x0fd9: 0x2f39, 0x0fda: 0x41da, 0x0fdb: 0x41de, 0x0fdc: 0x41e2, 0x0fdd: 0x41e7, + 0x0fde: 0x41e7, 0x0fdf: 0x41ec, 0x0fe0: 0x41f0, 0x0fe1: 0x41f4, 0x0fe2: 0x41f9, 0x0fe3: 0x41fd, + 0x0fe4: 0x4201, 0x0fe5: 0x4205, 0x0fe6: 0x420a, 0x0fe7: 0x420e, 0x0fe8: 0x4212, 0x0fe9: 0x4216, + 0x0fea: 0x421a, 0x0feb: 0x421e, 0x0fec: 0x4223, 0x0fed: 0x4227, 0x0fee: 0x422b, 0x0fef: 0x422f, + 0x0ff0: 0x4233, 0x0ff1: 0x4237, 0x0ff2: 0x423b, 0x0ff3: 0x4240, 0x0ff4: 0x4245, 0x0ff5: 0x4249, + 0x0ff6: 0x424e, 0x0ff7: 0x4252, 0x0ff8: 0x4257, 0x0ff9: 0x425b, 0x0ffa: 0x2f51, 0x0ffb: 0x425f, + 0x0ffc: 0x4264, 0x0ffd: 0x4269, 0x0ffe: 0x426d, 0x0fff: 0x4272, // Block 0x40, offset 0x1000 - 0x1000: 0x1657, 0x1001: 0x165d, 0x1004: 0x1663, 0x1005: 0x1669, - 0x1008: 0x166f, 0x1009: 0x1675, - 0x102c: 0x167b, 0x102d: 0x1681, 0x102e: 0x1687, 0x102f: 0x168d, + 0x1000: 0x4276, 0x1001: 0x427b, 0x1002: 0x427f, 0x1003: 0x4283, 0x1004: 0x4287, 0x1005: 0x428b, + 0x1006: 0x428f, 0x1007: 0x4293, 0x1008: 0x4298, 0x1009: 0x429d, 0x100a: 0x42a2, 0x100b: 0x3f14, + 0x100c: 0x42a7, 0x100d: 0x42ab, 0x100e: 0x42af, 0x100f: 0x42b3, 0x1010: 0x42b7, 0x1011: 0x42bb, + 0x1012: 0x42bf, 0x1013: 0x42c3, 0x1014: 0x42c7, 0x1015: 0x42cb, 0x1016: 0x42cf, 0x1017: 0x42d3, + 0x1018: 0x2c31, 0x1019: 0x42d8, 0x101a: 0x42dc, 0x101b: 0x42e0, 0x101c: 0x42e4, 0x101d: 0x42e8, + 0x101e: 0x42ec, 0x101f: 0x2f5d, 0x1020: 0x42f0, 0x1021: 0x42f4, 0x1022: 0x42f8, 0x1023: 0x42fc, + 0x1024: 0x4300, 0x1025: 0x4305, 0x1026: 0x430a, 0x1027: 0x430f, 0x1028: 0x4313, 0x1029: 0x4317, + 0x102a: 0x431b, 0x102b: 0x431f, 0x102c: 0x4324, 0x102d: 0x4328, 0x102e: 0x432d, 0x102f: 0x4331, + 0x1030: 0x4335, 0x1031: 0x433a, 0x1032: 0x433f, 0x1033: 0x4343, 0x1034: 0x2b45, 0x1035: 0x4347, + 0x1036: 0x434b, 0x1037: 0x434f, 0x1038: 0x4353, 0x1039: 0x4357, 0x103a: 0x435b, 0x103b: 0x306a, + 0x103c: 0x435f, 0x103d: 0x4363, 0x103e: 0x4367, 0x103f: 0x436b, // Block 0x41, offset 0x1040 - 0x1060: 0x1693, 0x1061: 0x1699, 0x1062: 0x169f, 0x1063: 0x16a5, - 0x106a: 0x16ab, 0x106b: 0x16b1, 0x106c: 0x16b7, 0x106d: 0x16bd, - // Block 0x42, offset 0x1080 - 0x10a9: 0x16c3, - 0x10aa: 0x16c7, - // Block 0x43, offset 0x10c0 - 0x10e0: 0x001c, 0x10e1: 0x000d, 0x10e2: 0x000f, 0x10e3: 0x1488, - 0x10e4: 0x148a, 0x10e5: 0x148c, 0x10e6: 0x148e, 0x10e7: 0x1490, 0x10e8: 0x1492, 0x10e9: 0x16cb, - 0x10ea: 0x16ce, 0x10eb: 0x16d1, 0x10ec: 0x16d4, 0x10ed: 0x16d7, 0x10ee: 0x16da, 0x10ef: 0x16dd, - 0x10f0: 0x16e0, 0x10f1: 0x16e3, 0x10f2: 0x16e6, 0x10f3: 0x16e9, 0x10f4: 0x16ec, 0x10f5: 0x16f0, - 0x10f6: 0x16f4, 0x10f7: 0x16f8, 0x10f8: 0x16fc, 0x10f9: 0x1700, 0x10fa: 0x1704, 0x10fb: 0x1708, - 0x10fc: 0x170c, 0x10fd: 0x1710, 0x10fe: 0x1715, 0x10ff: 0x171a, - // Block 0x44, offset 0x1100 - 0x1100: 0x171f, 0x1101: 0x1724, 0x1102: 0x1729, 0x1103: 0x172e, 0x1104: 0x1733, 0x1105: 0x1738, - 0x1106: 0x173d, 0x1107: 0x1742, 0x1108: 0x1747, 0x1109: 0x174a, 0x110a: 0x174d, 0x110b: 0x1750, - 0x110c: 0x1753, 0x110d: 0x1756, 0x110e: 0x1759, 0x110f: 0x175c, 0x1110: 0x175f, 0x1111: 0x1762, - 0x1112: 0x1766, 0x1113: 0x176a, 0x1114: 0x176e, 0x1115: 0x1772, 0x1116: 0x1776, 0x1117: 0x177a, - 0x1118: 0x177e, 0x1119: 0x1782, 0x111a: 0x1786, 0x111b: 0x178a, 0x111c: 0x178e, 0x111d: 0x1792, - 0x111e: 0x1796, 0x111f: 0x179a, 0x1120: 0x179e, 0x1121: 0x17a2, 0x1122: 0x17a6, 0x1123: 0x17aa, - 0x1124: 0x17ae, 0x1125: 0x17b2, 0x1126: 0x17b6, 0x1127: 0x17ba, 0x1128: 0x17be, 0x1129: 0x17c2, - 0x112a: 0x17c6, 0x112b: 0x17ca, 0x112c: 0x17ce, 0x112d: 0x17d2, 0x112e: 0x17d6, 0x112f: 0x17da, - 0x1130: 0x17de, 0x1131: 0x17e2, 0x1132: 0x17e6, 0x1133: 0x17ea, 0x1134: 0x17ee, 0x1135: 0x17f2, - 0x1136: 0x0906, 0x1137: 0x090b, 0x1138: 0x14ad, 0x1139: 0x090d, 0x113a: 0x090f, 0x113b: 0x14d9, - 0x113c: 0x0914, 0x113d: 0x0916, 0x113e: 0x0918, 0x113f: 0x091a, - // Block 0x45, offset 0x1140 - 0x1140: 0x091c, 0x1141: 0x091e, 0x1142: 0x0920, 0x1143: 0x0922, 0x1144: 0x0924, 0x1145: 0x0929, - 0x1146: 0x14c8, 0x1147: 0x092b, 0x1148: 0x17f6, 0x1149: 0x092d, 0x114a: 0x092f, 0x114b: 0x155f, - 0x114c: 0x0931, 0x114d: 0x1570, 0x114e: 0x17f8, 0x114f: 0x14d4, 0x1150: 0x0007, 0x1151: 0x093d, - 0x1152: 0x0984, 0x1153: 0x093f, 0x1154: 0x0941, 0x1155: 0x098c, 0x1156: 0x094c, 0x1157: 0x0494, - 0x1158: 0x097c, 0x1159: 0x0499, 0x115a: 0x094e, 0x115b: 0x04c5, 0x115c: 0x0950, 0x115d: 0x14a0, - 0x115e: 0x001e, 0x115f: 0x0960, 0x1160: 0x17fa, 0x1161: 0x049b, 0x1162: 0x02c8, 0x1163: 0x0962, - 0x1164: 0x0964, 0x1165: 0x096d, 0x1166: 0x04a6, 0x1167: 0x04c7, 0x1168: 0x04a8, 0x1169: 0x09df, - 0x116a: 0x1486, - // Block 0x46, offset 0x1180 - 0x118c: 0x17fc, - // Block 0x47, offset 0x11c0 - 0x11f4: 0x1809, 0x11f5: 0x180d, - 0x11f6: 0x1810, - // Block 0x48, offset 0x1200 - 0x121c: 0x1814, - // Block 0x49, offset 0x1240 - 0x127c: 0x0499, 0x127d: 0x155f, - // Block 0x4a, offset 0x1280 - 0x12af: 0x181a, - // Block 0x4b, offset 0x12c0 - 0x12df: 0x181e, - // Block 0x4c, offset 0x1300 - 0x1333: 0x1822, - // Block 0x4d, offset 0x1340 - 0x1340: 0x1826, 0x1341: 0x182a, 0x1342: 0x182e, 0x1343: 0x1832, 0x1344: 0x1836, 0x1345: 0x183a, - 0x1346: 0x183e, 0x1347: 0x1842, 0x1348: 0x1846, 0x1349: 0x184a, 0x134a: 0x184e, 0x134b: 0x1852, - 0x134c: 0x1856, 0x134d: 0x185a, 0x134e: 0x185e, 0x134f: 0x1862, 0x1350: 0x1866, 0x1351: 0x186a, - 0x1352: 0x186e, 0x1353: 0x1872, 0x1354: 0x1876, 0x1355: 0x187a, 0x1356: 0x187e, 0x1357: 0x1882, - 0x1358: 0x1886, 0x1359: 0x188a, 0x135a: 0x188e, 0x135b: 0x1892, 0x135c: 0x1896, 0x135d: 0x189a, - 0x135e: 0x189e, 0x135f: 0x18a2, 0x1360: 0x18a6, 0x1361: 0x18aa, 0x1362: 0x18ae, 0x1363: 0x18b2, - 0x1364: 0x18b6, 0x1365: 0x18ba, 0x1366: 0x18be, 0x1367: 0x18c2, 0x1368: 0x18c6, 0x1369: 0x18ca, - 0x136a: 0x18ce, 0x136b: 0x18d2, 0x136c: 0x18d6, 0x136d: 0x18da, 0x136e: 0x18de, 0x136f: 0x18e2, - 0x1370: 0x18e6, 0x1371: 0x18ea, 0x1372: 0x18ee, 0x1373: 0x18f2, 0x1374: 0x18f6, 0x1375: 0x18fa, - 0x1376: 0x18fe, 0x1377: 0x1902, 0x1378: 0x1906, 0x1379: 0x190a, 0x137a: 0x190e, 0x137b: 0x1912, - 0x137c: 0x1916, 0x137d: 0x191a, 0x137e: 0x191e, 0x137f: 0x1922, - // Block 0x4e, offset 0x1380 - 0x1380: 0x1926, 0x1381: 0x192a, 0x1382: 0x192e, 0x1383: 0x1932, 0x1384: 0x1936, 0x1385: 0x193a, - 0x1386: 0x193e, 0x1387: 0x1942, 0x1388: 0x1946, 0x1389: 0x194a, 0x138a: 0x194e, 0x138b: 0x1952, - 0x138c: 0x1956, 0x138d: 0x195a, 0x138e: 0x195e, 0x138f: 0x1962, 0x1390: 0x1966, 0x1391: 0x196a, - 0x1392: 0x196e, 0x1393: 0x1972, 0x1394: 0x1976, 0x1395: 0x197a, 0x1396: 0x197e, 0x1397: 0x1982, - 0x1398: 0x1986, 0x1399: 0x198a, 0x139a: 0x198e, 0x139b: 0x1992, 0x139c: 0x1996, 0x139d: 0x199a, - 0x139e: 0x199e, 0x139f: 0x19a2, 0x13a0: 0x19a6, 0x13a1: 0x19aa, 0x13a2: 0x19ae, 0x13a3: 0x19b2, - 0x13a4: 0x19b6, 0x13a5: 0x19ba, 0x13a6: 0x19be, 0x13a7: 0x19c2, 0x13a8: 0x19c6, 0x13a9: 0x19ca, - 0x13aa: 0x19ce, 0x13ab: 0x19d2, 0x13ac: 0x19d6, 0x13ad: 0x19da, 0x13ae: 0x19de, 0x13af: 0x19e2, - 0x13b0: 0x19e6, 0x13b1: 0x19ea, 0x13b2: 0x19ee, 0x13b3: 0x19f2, 0x13b4: 0x19f6, 0x13b5: 0x19fa, - 0x13b6: 0x19fe, 0x13b7: 0x1a02, 0x13b8: 0x1a06, 0x13b9: 0x1a0a, 0x13ba: 0x1a0e, 0x13bb: 0x1a12, - 0x13bc: 0x1a16, 0x13bd: 0x1a1a, 0x13be: 0x1a1e, 0x13bf: 0x1a22, - // Block 0x4f, offset 0x13c0 - 0x13c0: 0x1a26, 0x13c1: 0x1a2a, 0x13c2: 0x1a2e, 0x13c3: 0x1a32, 0x13c4: 0x1a36, 0x13c5: 0x1a3a, - 0x13c6: 0x1a3e, 0x13c7: 0x1a42, 0x13c8: 0x1a46, 0x13c9: 0x1a4a, 0x13ca: 0x1a4e, 0x13cb: 0x1a52, - 0x13cc: 0x1a56, 0x13cd: 0x1a5a, 0x13ce: 0x1a5e, 0x13cf: 0x1a62, 0x13d0: 0x1a66, 0x13d1: 0x1a6a, - 0x13d2: 0x1a6e, 0x13d3: 0x1a72, 0x13d4: 0x1a76, 0x13d5: 0x1a7a, 0x13d6: 0x1a7e, 0x13d7: 0x1a82, - 0x13d8: 0x1a86, 0x13d9: 0x1a8a, 0x13da: 0x1a8e, 0x13db: 0x1a92, 0x13dc: 0x1a96, 0x13dd: 0x1a9a, - 0x13de: 0x1a9e, 0x13df: 0x1aa2, 0x13e0: 0x1aa6, 0x13e1: 0x1aaa, 0x13e2: 0x1aae, 0x13e3: 0x1ab2, - 0x13e4: 0x1ab6, 0x13e5: 0x1aba, 0x13e6: 0x1abe, 0x13e7: 0x1ac2, 0x13e8: 0x1ac6, 0x13e9: 0x1aca, - 0x13ea: 0x1ace, 0x13eb: 0x1ad2, 0x13ec: 0x1ad6, 0x13ed: 0x1ada, 0x13ee: 0x1ade, 0x13ef: 0x1ae2, - 0x13f0: 0x1ae6, 0x13f1: 0x1aea, 0x13f2: 0x1aee, 0x13f3: 0x1af2, 0x13f4: 0x1af6, 0x13f5: 0x1afa, - 0x13f6: 0x1afe, 0x13f7: 0x1b02, 0x13f8: 0x1b06, 0x13f9: 0x1b0a, 0x13fa: 0x1b0e, 0x13fb: 0x1b12, - 0x13fc: 0x1b16, 0x13fd: 0x1b1a, 0x13fe: 0x1b1e, 0x13ff: 0x1b22, - // Block 0x50, offset 0x1400 - 0x1400: 0x1b26, 0x1401: 0x1b2a, 0x1402: 0x1b2e, 0x1403: 0x1b32, 0x1404: 0x1b36, 0x1405: 0x1b3a, - 0x1406: 0x1b3e, 0x1407: 0x1b42, 0x1408: 0x1b46, 0x1409: 0x1b4a, 0x140a: 0x1b4e, 0x140b: 0x1b52, - 0x140c: 0x1b56, 0x140d: 0x1b5a, 0x140e: 0x1b5e, 0x140f: 0x1b62, 0x1410: 0x1b66, 0x1411: 0x1b6a, - 0x1412: 0x1b6e, 0x1413: 0x1b72, 0x1414: 0x1b76, 0x1415: 0x1b7a, - // Block 0x51, offset 0x1440 - 0x1440: 0x0001, - 0x1476: 0x1b7e, 0x1478: 0x1882, 0x1479: 0x1b82, 0x147a: 0x1b86, - // Block 0x52, offset 0x1480 - 0x148c: 0x1b8a, 0x148e: 0x1b91, 0x1490: 0x1b98, - 0x1492: 0x1b9f, 0x1494: 0x1ba6, 0x1496: 0x1bad, - 0x1498: 0x1bb4, 0x149a: 0x1bbb, 0x149c: 0x1bc2, - 0x149e: 0x1bc9, 0x14a0: 0x1bd0, 0x14a2: 0x1bd7, - 0x14a5: 0x1bde, 0x14a7: 0x1be5, 0x14a9: 0x1bec, - 0x14b0: 0x1bf3, 0x14b1: 0x1bfa, 0x14b3: 0x1c01, 0x14b4: 0x1c08, - 0x14b6: 0x1c0f, 0x14b7: 0x1c16, 0x14b9: 0x1c1d, 0x14ba: 0x1c24, - 0x14bc: 0x1c2b, 0x14bd: 0x1c32, - // Block 0x53, offset 0x14c0 - 0x14d4: 0x1c39, - 0x14db: 0x1c40, 0x14dc: 0x1c45, - 0x14de: 0x1c4a, 0x14df: 0x1c51, - 0x14ec: 0x1c58, 0x14ee: 0x1c5f, - 0x14f0: 0x1c66, 0x14f2: 0x1c6d, 0x14f4: 0x1c74, - 0x14f6: 0x1c7b, 0x14f8: 0x1c82, 0x14fa: 0x1c89, - 0x14fc: 0x1c90, 0x14fe: 0x1c97, - // Block 0x54, offset 0x1500 - 0x1500: 0x1c9e, 0x1502: 0x1ca5, 0x1505: 0x1cac, - 0x1507: 0x1cb3, 0x1509: 0x1cba, - 0x1510: 0x1cc1, 0x1511: 0x1cc8, - 0x1513: 0x1ccf, 0x1514: 0x1cd6, 0x1516: 0x1cdd, 0x1517: 0x1ce4, - 0x1519: 0x1ceb, 0x151a: 0x1cf2, 0x151c: 0x1cf9, 0x151d: 0x1d00, - 0x1534: 0x1d07, - 0x1537: 0x1d0e, 0x1538: 0x1d15, 0x1539: 0x1d1c, 0x153a: 0x1d23, - 0x153e: 0x1d2a, 0x153f: 0x1d31, - // Block 0x55, offset 0x1540 - 0x1571: 0x1d38, 0x1572: 0x1d3c, 0x1573: 0x1d40, 0x1574: 0x1d44, 0x1575: 0x1d48, - 0x1576: 0x1d4c, 0x1577: 0x1d50, 0x1578: 0x1d54, 0x1579: 0x1d58, 0x157a: 0x1d5c, 0x157b: 0x1d60, - 0x157c: 0x1d64, 0x157d: 0x1d68, 0x157e: 0x1d6c, 0x157f: 0x1d70, - // Block 0x56, offset 0x1580 - 0x1580: 0x1d74, 0x1581: 0x1d78, 0x1582: 0x1d7c, 0x1583: 0x1d80, 0x1584: 0x1d84, 0x1585: 0x1d88, - 0x1586: 0x1d8c, 0x1587: 0x1d90, 0x1588: 0x1d94, 0x1589: 0x1d98, 0x158a: 0x1d9c, 0x158b: 0x1da0, - 0x158c: 0x1da4, 0x158d: 0x1da8, 0x158e: 0x1dac, 0x158f: 0x1db0, 0x1590: 0x1db4, 0x1591: 0x1db8, - 0x1592: 0x1dbc, 0x1593: 0x1dc0, 0x1594: 0x1dc4, 0x1595: 0x1dc8, 0x1596: 0x1dcc, 0x1597: 0x1dd0, - 0x1598: 0x1dd4, 0x1599: 0x1dd8, 0x159a: 0x1ddc, 0x159b: 0x1de0, 0x159c: 0x1de4, 0x159d: 0x1de8, - 0x159e: 0x1dec, 0x159f: 0x1df0, 0x15a0: 0x1df4, 0x15a1: 0x1df8, 0x15a2: 0x1dfc, 0x15a3: 0x1e00, - 0x15a4: 0x1e04, 0x15a5: 0x1e08, 0x15a6: 0x1e0c, 0x15a7: 0x1e10, 0x15a8: 0x1e14, 0x15a9: 0x1e18, - 0x15aa: 0x1e1c, 0x15ab: 0x1e20, 0x15ac: 0x1e24, 0x15ad: 0x1e28, 0x15ae: 0x1e2c, 0x15af: 0x1e30, - 0x15b0: 0x1e34, 0x15b1: 0x1e38, 0x15b2: 0x1e3c, 0x15b3: 0x1e40, 0x15b4: 0x1e44, 0x15b5: 0x1e48, - 0x15b6: 0x1e4c, 0x15b7: 0x1e50, 0x15b8: 0x1e54, 0x15b9: 0x1e58, 0x15ba: 0x1e5c, 0x15bb: 0x1e60, - 0x15bc: 0x1e64, 0x15bd: 0x1e68, 0x15be: 0x1e6c, 0x15bf: 0x1e70, - // Block 0x57, offset 0x15c0 - 0x15c0: 0x1e74, 0x15c1: 0x1e78, 0x15c2: 0x1e7c, 0x15c3: 0x1e80, 0x15c4: 0x1e84, 0x15c5: 0x1e88, - 0x15c6: 0x1e8c, 0x15c7: 0x1e90, 0x15c8: 0x1e94, 0x15c9: 0x1e98, 0x15ca: 0x1e9c, 0x15cb: 0x1ea0, - 0x15cc: 0x1ea4, 0x15cd: 0x1ea8, 0x15ce: 0x1eac, - 0x15d2: 0x1826, 0x15d3: 0x183e, 0x15d4: 0x1eb0, 0x15d5: 0x1eb4, 0x15d6: 0x1eb8, 0x15d7: 0x1ebc, - 0x15d8: 0x1ec0, 0x15d9: 0x1ec4, 0x15da: 0x1836, 0x15db: 0x1ec8, 0x15dc: 0x1ecc, 0x15dd: 0x1ed0, - 0x15de: 0x1ed4, 0x15df: 0x1846, - // Block 0x58, offset 0x1600 - 0x1600: 0x1ed8, 0x1601: 0x1ede, 0x1602: 0x1ee4, 0x1603: 0x1eea, 0x1604: 0x1ef0, 0x1605: 0x1ef6, - 0x1606: 0x1efc, 0x1607: 0x1f02, 0x1608: 0x1f08, 0x1609: 0x1f0e, 0x160a: 0x1f14, 0x160b: 0x1f1a, - 0x160c: 0x1f20, 0x160d: 0x1f26, 0x160e: 0x1f2c, 0x160f: 0x1f35, 0x1610: 0x1f3e, 0x1611: 0x1f47, - 0x1612: 0x1f50, 0x1613: 0x1f59, 0x1614: 0x1f62, 0x1615: 0x1f6b, 0x1616: 0x1f74, 0x1617: 0x1f7d, - 0x1618: 0x1f86, 0x1619: 0x1f8f, 0x161a: 0x1f98, 0x161b: 0x1fa1, 0x161c: 0x1faa, 0x161d: 0x1fb3, - 0x161e: 0x1fc5, 0x1620: 0x1fd4, 0x1621: 0x1fda, 0x1622: 0x1fe0, 0x1623: 0x1fe6, - 0x1624: 0x1fec, 0x1625: 0x1ff2, 0x1626: 0x1ff8, 0x1627: 0x1ffe, 0x1628: 0x2004, 0x1629: 0x200a, - 0x162a: 0x2010, 0x162b: 0x2016, 0x162c: 0x201c, 0x162d: 0x2022, 0x162e: 0x2028, 0x162f: 0x202e, - 0x1630: 0x2034, 0x1631: 0x203a, 0x1632: 0x2040, 0x1633: 0x2046, 0x1634: 0x204c, 0x1635: 0x2052, - 0x1636: 0x2058, 0x1637: 0x205e, 0x1638: 0x2064, 0x1639: 0x206a, 0x163a: 0x2070, 0x163b: 0x2076, - 0x163c: 0x207c, 0x163d: 0x2082, 0x163e: 0x2088, 0x163f: 0x208e, - // Block 0x59, offset 0x1640 - 0x1640: 0x2094, 0x1641: 0x209a, 0x1642: 0x20a0, 0x1643: 0x20a6, 0x1644: 0x20ac, 0x1645: 0x20b0, - 0x1646: 0x192e, 0x1647: 0x20b4, - 0x1650: 0x20b8, 0x1651: 0x20bc, - 0x1652: 0x20bf, 0x1653: 0x20c2, 0x1654: 0x20c5, 0x1655: 0x20c8, 0x1656: 0x20cb, 0x1657: 0x20ce, - 0x1658: 0x20d1, 0x1659: 0x20d4, 0x165a: 0x20d7, 0x165b: 0x20da, 0x165c: 0x20dd, 0x165d: 0x20e0, - 0x165e: 0x20e3, 0x165f: 0x20e6, 0x1660: 0x1d38, 0x1661: 0x1d44, 0x1662: 0x1d50, 0x1663: 0x1d58, - 0x1664: 0x1d78, 0x1665: 0x1d7c, 0x1666: 0x1d88, 0x1667: 0x1d90, 0x1668: 0x1d94, 0x1669: 0x1d9c, - 0x166a: 0x1da0, 0x166b: 0x1da4, 0x166c: 0x1da8, 0x166d: 0x1dac, 0x166e: 0x20e9, 0x166f: 0x20f0, - 0x1670: 0x20f7, 0x1671: 0x20fe, 0x1672: 0x2105, 0x1673: 0x210c, 0x1674: 0x2113, 0x1675: 0x211a, - 0x1676: 0x2121, 0x1677: 0x2128, 0x1678: 0x212f, 0x1679: 0x2136, 0x167a: 0x213d, 0x167b: 0x2144, - 0x167c: 0x214b, 0x167d: 0x215b, 0x167e: 0x2168, - // Block 0x5a, offset 0x1680 - 0x1680: 0x1826, 0x1681: 0x183e, 0x1682: 0x1eb0, 0x1683: 0x1eb4, 0x1684: 0x216f, 0x1685: 0x2173, - 0x1686: 0x2177, 0x1687: 0x1852, 0x1688: 0x217b, 0x1689: 0x1882, 0x168a: 0x194a, 0x168b: 0x197a, - 0x168c: 0x1976, 0x168d: 0x194e, 0x168e: 0x1abe, 0x168f: 0x18a2, 0x1690: 0x1942, 0x1691: 0x217f, - 0x1692: 0x2183, 0x1693: 0x2187, 0x1694: 0x218b, 0x1695: 0x218f, 0x1696: 0x2193, 0x1697: 0x2197, - 0x1698: 0x219b, 0x1699: 0x219f, 0x169a: 0x21a3, 0x169b: 0x18ba, 0x169c: 0x21a7, 0x169d: 0x21ab, - 0x169e: 0x21af, 0x169f: 0x21b3, 0x16a0: 0x21b7, 0x16a1: 0x21bb, 0x16a2: 0x21bf, 0x16a3: 0x21c3, - 0x16a4: 0x1eb8, 0x16a5: 0x1ebc, 0x16a6: 0x1ec0, 0x16a7: 0x21c7, 0x16a8: 0x21cb, 0x16a9: 0x21cf, - 0x16aa: 0x21d3, 0x16ab: 0x21d7, 0x16ac: 0x21db, 0x16ad: 0x21df, 0x16ae: 0x21e3, 0x16af: 0x21e7, - 0x16b0: 0x21eb, 0x16b1: 0x21ef, 0x16b2: 0x21f2, 0x16b3: 0x21f5, 0x16b4: 0x21f8, 0x16b5: 0x21fb, - 0x16b6: 0x21fe, 0x16b7: 0x2201, 0x16b8: 0x2204, 0x16b9: 0x2207, 0x16ba: 0x220a, 0x16bb: 0x220d, - 0x16bc: 0x2210, 0x16bd: 0x2213, 0x16be: 0x2216, 0x16bf: 0x2219, - // Block 0x5b, offset 0x16c0 - 0x16c0: 0x221c, 0x16c1: 0x2221, 0x16c2: 0x2226, 0x16c3: 0x222b, 0x16c4: 0x2230, 0x16c5: 0x2235, - 0x16c6: 0x223a, 0x16c7: 0x223f, 0x16c8: 0x2244, 0x16c9: 0x2249, 0x16ca: 0x224f, 0x16cb: 0x2255, - 0x16cc: 0x225b, 0x16cd: 0x225e, 0x16ce: 0x2262, 0x16cf: 0x2265, 0x16d0: 0x2269, 0x16d1: 0x226d, - 0x16d2: 0x2271, 0x16d3: 0x2275, 0x16d4: 0x2279, 0x16d5: 0x227d, 0x16d6: 0x2281, 0x16d7: 0x2285, - 0x16d8: 0x2289, 0x16d9: 0x228d, 0x16da: 0x2291, 0x16db: 0x2295, 0x16dc: 0x2299, 0x16dd: 0x229d, - 0x16de: 0x22a1, 0x16df: 0x22a5, 0x16e0: 0x22a9, 0x16e1: 0x22ad, 0x16e2: 0x22b1, 0x16e3: 0x22b5, - 0x16e4: 0x22b9, 0x16e5: 0x22bd, 0x16e6: 0x22c1, 0x16e7: 0x22c5, 0x16e8: 0x22c9, 0x16e9: 0x22cd, - 0x16ea: 0x22d1, 0x16eb: 0x22d5, 0x16ec: 0x22d9, 0x16ed: 0x22dd, 0x16ee: 0x22e1, 0x16ef: 0x22e5, - 0x16f0: 0x22e9, 0x16f1: 0x22ed, 0x16f2: 0x22f1, 0x16f3: 0x22f5, 0x16f4: 0x22f9, 0x16f5: 0x22fd, - 0x16f6: 0x2301, 0x16f7: 0x2305, 0x16f8: 0x2309, 0x16f9: 0x230d, 0x16fa: 0x2311, 0x16fb: 0x2315, - 0x16fc: 0x2319, 0x16fd: 0x231d, 0x16fe: 0x2321, - // Block 0x5c, offset 0x1700 - 0x1700: 0x2325, 0x1701: 0x2335, 0x1702: 0x2342, 0x1703: 0x2352, 0x1704: 0x235c, 0x1705: 0x236c, - 0x1706: 0x2376, 0x1707: 0x2380, 0x1708: 0x2393, 0x1709: 0x23a0, 0x170a: 0x23aa, 0x170b: 0x23b4, - 0x170c: 0x23be, 0x170d: 0x23cb, 0x170e: 0x23d8, 0x170f: 0x23e5, 0x1710: 0x23f2, 0x1711: 0x23ff, - 0x1712: 0x240c, 0x1713: 0x2419, 0x1714: 0x242c, 0x1715: 0x2433, 0x1716: 0x2446, 0x1717: 0x2459, - 0x1718: 0x2469, 0x1719: 0x2476, 0x171a: 0x2489, 0x171b: 0x249c, 0x171c: 0x24a9, 0x171d: 0x24b3, - 0x171e: 0x24bd, 0x171f: 0x24ca, 0x1720: 0x24d7, 0x1721: 0x24e7, 0x1722: 0x24f7, 0x1723: 0x2501, - 0x1724: 0x250b, 0x1725: 0x2518, 0x1726: 0x2522, 0x1727: 0x252c, 0x1728: 0x2533, 0x1729: 0x253a, - 0x172a: 0x2544, 0x172b: 0x254e, 0x172c: 0x2561, 0x172d: 0x256e, 0x172e: 0x257e, 0x172f: 0x2591, - 0x1730: 0x259e, 0x1731: 0x25a8, 0x1732: 0x25b2, 0x1733: 0x25c5, 0x1734: 0x25d2, 0x1735: 0x25e5, - 0x1736: 0x25ef, 0x1737: 0x25ff, 0x1738: 0x2609, 0x1739: 0x2616, 0x173a: 0x2620, 0x173b: 0x262d, - 0x173c: 0x263d, 0x173d: 0x264a, 0x173e: 0x265a, 0x173f: 0x2667, - // Block 0x5d, offset 0x1740 - 0x1740: 0x266e, 0x1741: 0x267e, 0x1742: 0x2688, 0x1743: 0x2692, 0x1744: 0x269f, 0x1745: 0x26a9, - 0x1746: 0x26b3, 0x1747: 0x26bd, 0x1748: 0x26cd, 0x1749: 0x26da, 0x174a: 0x26e1, 0x174b: 0x26f4, - 0x174c: 0x26fe, 0x174d: 0x270e, 0x174e: 0x271b, 0x174f: 0x2728, 0x1750: 0x2732, 0x1751: 0x273c, - 0x1752: 0x2749, 0x1753: 0x2750, 0x1754: 0x275d, 0x1755: 0x276d, 0x1756: 0x2774, 0x1757: 0x2787, - 0x1758: 0x2791, 0x1759: 0x2796, 0x175a: 0x279b, 0x175b: 0x27a0, 0x175c: 0x27a5, 0x175d: 0x27aa, - 0x175e: 0x27af, 0x175f: 0x27b4, 0x1760: 0x27b9, 0x1761: 0x27be, 0x1762: 0x27c3, 0x1763: 0x27c9, - 0x1764: 0x27cf, 0x1765: 0x27d5, 0x1766: 0x27db, 0x1767: 0x27e1, 0x1768: 0x27e7, 0x1769: 0x27ed, - 0x176a: 0x27f3, 0x176b: 0x27f9, 0x176c: 0x27ff, 0x176d: 0x2805, 0x176e: 0x280b, 0x176f: 0x2811, - 0x1770: 0x2817, 0x1771: 0x281d, 0x1772: 0x2821, 0x1773: 0x2824, 0x1774: 0x2827, 0x1775: 0x282b, - 0x1776: 0x282e, 0x1777: 0x2831, 0x1778: 0x2834, 0x1779: 0x2838, 0x177a: 0x283c, 0x177b: 0x283f, - 0x177c: 0x2846, 0x177d: 0x284d, 0x177e: 0x2854, 0x177f: 0x285b, - // Block 0x5e, offset 0x1780 - 0x1780: 0x2868, 0x1781: 0x286b, 0x1782: 0x286e, 0x1783: 0x2872, 0x1784: 0x2875, 0x1785: 0x2878, - 0x1786: 0x287b, 0x1787: 0x287e, 0x1788: 0x2881, 0x1789: 0x2885, 0x178a: 0x288a, 0x178b: 0x288d, - 0x178c: 0x2890, 0x178d: 0x2894, 0x178e: 0x2898, 0x178f: 0x289b, 0x1790: 0x289e, 0x1791: 0x28a1, - 0x1792: 0x28a5, 0x1793: 0x28a9, 0x1794: 0x28ad, 0x1795: 0x28b1, 0x1796: 0x28b5, 0x1797: 0x28b8, - 0x1798: 0x28bb, 0x1799: 0x28be, 0x179a: 0x28c1, 0x179b: 0x28c4, 0x179c: 0x28c8, 0x179d: 0x28cb, - 0x179e: 0x28ce, 0x179f: 0x28d1, 0x17a0: 0x28d5, 0x17a1: 0x28d9, 0x17a2: 0x28dc, 0x17a3: 0x28e0, - 0x17a4: 0x28e4, 0x17a5: 0x28e8, 0x17a6: 0x28eb, 0x17a7: 0x28ef, 0x17a8: 0x28f5, 0x17a9: 0x28fc, - 0x17aa: 0x28ff, 0x17ab: 0x2903, 0x17ac: 0x2907, 0x17ad: 0x290b, 0x17ae: 0x290f, 0x17af: 0x2917, - 0x17b0: 0x2920, 0x17b1: 0x2923, 0x17b2: 0x2926, 0x17b3: 0x292a, 0x17b4: 0x292d, 0x17b5: 0x2930, - 0x17b6: 0x2933, 0x17b7: 0x2937, 0x17b8: 0x293a, 0x17b9: 0x293d, 0x17ba: 0x2940, 0x17bb: 0x2943, - 0x17bc: 0x2946, 0x17bd: 0x294a, 0x17be: 0x294d, 0x17bf: 0x2950, - // Block 0x5f, offset 0x17c0 - 0x17c0: 0x2953, 0x17c1: 0x2957, 0x17c2: 0x295b, 0x17c3: 0x2960, 0x17c4: 0x2963, 0x17c5: 0x2966, - 0x17c6: 0x2969, 0x17c7: 0x2970, 0x17c8: 0x2974, 0x17c9: 0x2977, 0x17ca: 0x297a, 0x17cb: 0x297d, - 0x17cc: 0x2980, 0x17cd: 0x2983, 0x17ce: 0x2986, 0x17cf: 0x2989, 0x17d0: 0x298c, 0x17d1: 0x298f, - 0x17d2: 0x2992, 0x17d3: 0x2996, 0x17d4: 0x2999, 0x17d5: 0x299c, 0x17d6: 0x29a0, 0x17d7: 0x29a4, - 0x17d8: 0x29a7, 0x17d9: 0x29ac, 0x17da: 0x29b0, 0x17db: 0x29b3, 0x17dc: 0x29b6, 0x17dd: 0x29b9, - 0x17de: 0x29bc, 0x17df: 0x29c2, 0x17e0: 0x29c8, 0x17e1: 0x29cd, 0x17e2: 0x29d2, 0x17e3: 0x29d7, - 0x17e4: 0x29dc, 0x17e5: 0x29e1, 0x17e6: 0x29e6, 0x17e7: 0x29eb, 0x17e8: 0x29f0, 0x17e9: 0x29f5, - 0x17ea: 0x29fb, 0x17eb: 0x2a01, 0x17ec: 0x2a07, 0x17ed: 0x2a0d, 0x17ee: 0x2a13, 0x17ef: 0x2a19, - 0x17f0: 0x2a1f, 0x17f1: 0x2a25, 0x17f2: 0x2a2b, 0x17f3: 0x2a31, 0x17f4: 0x2a37, 0x17f5: 0x2a3d, - 0x17f6: 0x2a43, 0x17f7: 0x2a49, 0x17f8: 0x2a4f, 0x17f9: 0x2a55, 0x17fa: 0x2a5b, 0x17fb: 0x2a61, - 0x17fc: 0x2a67, 0x17fd: 0x2a6d, 0x17fe: 0x2a73, 0x17ff: 0x2a79, - // Block 0x60, offset 0x1800 - 0x1830: 0x2a7d, - // Block 0x61, offset 0x1840 - 0x1840: 0x2a81, 0x1841: 0x2a85, 0x1842: 0x1a9e, 0x1843: 0x2a89, 0x1844: 0x2a8d, 0x1845: 0x2a91, - 0x1846: 0x2a95, 0x1847: 0x1b76, 0x1848: 0x1b76, 0x1849: 0x2a99, 0x184a: 0x1abe, 0x184b: 0x2a9d, - 0x184c: 0x2aa1, 0x184d: 0x2aa5, 0x184e: 0x2aa9, 0x184f: 0x2aad, 0x1850: 0x2ab1, 0x1851: 0x2ab5, - 0x1852: 0x2ab9, 0x1853: 0x2abd, 0x1854: 0x2ac1, 0x1855: 0x2ac5, 0x1856: 0x2ac9, 0x1857: 0x2acd, - 0x1858: 0x2ad1, 0x1859: 0x2ad5, 0x185a: 0x2ad9, 0x185b: 0x2add, 0x185c: 0x2ae1, 0x185d: 0x2ae5, - 0x185e: 0x2ae9, 0x185f: 0x2aed, 0x1860: 0x2af1, 0x1861: 0x2af5, 0x1862: 0x2af9, 0x1863: 0x2afd, - 0x1864: 0x2b01, 0x1865: 0x2b05, 0x1866: 0x2b09, 0x1867: 0x2b0d, 0x1868: 0x2b11, 0x1869: 0x2b15, - 0x186a: 0x2b19, 0x186b: 0x2b1d, 0x186c: 0x2b21, 0x186d: 0x2b25, 0x186e: 0x2b29, 0x186f: 0x2b2d, - 0x1870: 0x2b31, 0x1871: 0x2b35, 0x1872: 0x2b39, 0x1873: 0x2b3d, 0x1874: 0x1a16, 0x1875: 0x2b41, - 0x1876: 0x2b45, 0x1877: 0x2b49, 0x1878: 0x2b4d, 0x1879: 0x2b51, 0x187a: 0x2b55, 0x187b: 0x2b59, - 0x187c: 0x2b5d, 0x187d: 0x2b61, 0x187e: 0x2b65, 0x187f: 0x2b69, - // Block 0x62, offset 0x1880 - 0x1880: 0x1b3a, 0x1881: 0x2b6d, 0x1882: 0x2b71, 0x1883: 0x2b75, 0x1884: 0x2b79, 0x1885: 0x2b7d, - 0x1886: 0x2b81, 0x1887: 0x2b85, 0x1888: 0x2b89, 0x1889: 0x2b8d, 0x188a: 0x2b91, 0x188b: 0x2b95, - 0x188c: 0x2b99, 0x188d: 0x2b9d, 0x188e: 0x2ba1, 0x188f: 0x2ba5, 0x1890: 0x2ba9, 0x1891: 0x2bad, - 0x1892: 0x2bb1, 0x1893: 0x2bb5, 0x1894: 0x2bb9, 0x1895: 0x2bbd, 0x1896: 0x2bc1, 0x1897: 0x2bc5, - 0x1898: 0x2bc9, 0x1899: 0x2bcd, 0x189a: 0x2bd1, 0x189b: 0x2bd5, 0x189c: 0x2ac1, 0x189d: 0x2bd9, - 0x189e: 0x2bdd, 0x189f: 0x2be1, 0x18a0: 0x2be5, 0x18a1: 0x2be9, 0x18a2: 0x2bed, 0x18a3: 0x2bf1, - 0x18a4: 0x2bf5, 0x18a5: 0x2bf9, 0x18a6: 0x2bfd, 0x18a7: 0x2c01, 0x18a8: 0x2c05, 0x18a9: 0x2c09, - 0x18aa: 0x2c0d, 0x18ab: 0x2c11, 0x18ac: 0x2c15, 0x18ad: 0x2c19, 0x18ae: 0x2c1d, 0x18af: 0x2c21, - 0x18b0: 0x2c25, 0x18b1: 0x1aa6, 0x18b2: 0x2c29, 0x18b3: 0x2c2d, 0x18b4: 0x2c31, 0x18b5: 0x2c35, - 0x18b6: 0x2c39, 0x18b7: 0x2c3d, 0x18b8: 0x2c41, 0x18b9: 0x2c45, 0x18ba: 0x2c49, 0x18bb: 0x2c4d, - 0x18bc: 0x2c51, 0x18bd: 0x2c55, 0x18be: 0x2c59, 0x18bf: 0x2c5d, - // Block 0x63, offset 0x18c0 - 0x18c0: 0x2c61, 0x18c1: 0x18ba, 0x18c2: 0x2c65, 0x18c3: 0x2c69, 0x18c4: 0x2c6d, 0x18c5: 0x2c71, - 0x18c6: 0x2c75, 0x18c7: 0x2c79, 0x18c8: 0x2c7d, 0x18c9: 0x2c81, 0x18ca: 0x186e, 0x18cb: 0x2c85, - 0x18cc: 0x2c89, 0x18cd: 0x2c8d, 0x18ce: 0x2c91, 0x18cf: 0x2c95, 0x18d0: 0x2c99, 0x18d1: 0x2c9d, - 0x18d2: 0x2ca1, 0x18d3: 0x2ca5, 0x18d4: 0x2ca9, 0x18d5: 0x2cad, 0x18d6: 0x2cb1, 0x18d7: 0x2cb5, - 0x18d8: 0x2cb9, 0x18d9: 0x2cbd, 0x18da: 0x2cc1, 0x18db: 0x2cc5, 0x18dc: 0x2cc9, 0x18dd: 0x2ccd, - 0x18de: 0x2cd1, 0x18df: 0x2cd5, 0x18e0: 0x2cd9, 0x18e1: 0x2c21, 0x18e2: 0x2cdd, 0x18e3: 0x2ce1, - 0x18e4: 0x2ce5, 0x18e5: 0x2ce9, 0x18e6: 0x2ced, 0x18e7: 0x2cf1, 0x18e8: 0x2cf5, 0x18e9: 0x2cf9, - 0x18ea: 0x2be1, 0x18eb: 0x2cfd, 0x18ec: 0x2d01, 0x18ed: 0x2d05, 0x18ee: 0x2d09, 0x18ef: 0x2d0d, - 0x18f0: 0x2d11, 0x18f1: 0x2d15, 0x18f2: 0x2d19, 0x18f3: 0x2d1d, 0x18f4: 0x2d21, 0x18f5: 0x2d25, - 0x18f6: 0x2d29, 0x18f7: 0x2d2d, 0x18f8: 0x2d31, 0x18f9: 0x2d35, 0x18fa: 0x2d39, 0x18fb: 0x2d3d, - 0x18fc: 0x2d41, 0x18fd: 0x2d45, 0x18fe: 0x2d49, 0x18ff: 0x2ac1, - // Block 0x64, offset 0x1900 - 0x1900: 0x2d4d, 0x1901: 0x2d51, 0x1902: 0x2d55, 0x1903: 0x2d59, 0x1904: 0x1b72, 0x1905: 0x2d5d, - 0x1906: 0x2d61, 0x1907: 0x2d65, 0x1908: 0x2d69, 0x1909: 0x2d6d, 0x190a: 0x2d71, 0x190b: 0x2d75, - 0x190c: 0x2d79, 0x190d: 0x2d7d, 0x190e: 0x2d81, 0x190f: 0x2d85, 0x1910: 0x2d89, 0x1911: 0x2173, - 0x1912: 0x2d8d, 0x1913: 0x2d91, 0x1914: 0x2d95, 0x1915: 0x2d99, 0x1916: 0x2d9d, 0x1917: 0x2da1, - 0x1918: 0x2da5, 0x1919: 0x2da9, 0x191a: 0x2dad, 0x191b: 0x2be9, 0x191c: 0x2db1, 0x191d: 0x2db5, - 0x191e: 0x2db9, 0x191f: 0x2dbd, 0x1920: 0x2dc1, 0x1921: 0x2dc5, 0x1922: 0x2dc9, 0x1923: 0x2dcd, - 0x1924: 0x2dd1, 0x1925: 0x2dd5, 0x1926: 0x2dd9, 0x1927: 0x2ddd, 0x1928: 0x2de1, 0x1929: 0x1aba, - 0x192a: 0x2de5, 0x192b: 0x2de9, 0x192c: 0x2ded, 0x192d: 0x2df1, 0x192e: 0x2df5, 0x192f: 0x2df9, - 0x1930: 0x2dfd, 0x1931: 0x2e01, 0x1932: 0x2e05, 0x1933: 0x2e09, 0x1934: 0x2e0d, 0x1935: 0x2e11, - 0x1936: 0x2e15, 0x1937: 0x19f6, 0x1938: 0x2e19, 0x1939: 0x2e1d, 0x193a: 0x2e21, 0x193b: 0x2e25, - 0x193c: 0x2e29, 0x193d: 0x2e2d, 0x193e: 0x2e31, 0x193f: 0x2e35, - // Block 0x65, offset 0x1940 - 0x1940: 0x2e39, 0x1941: 0x2e3d, 0x1942: 0x2e41, 0x1943: 0x2e45, 0x1944: 0x2e49, 0x1945: 0x2e4d, - 0x1946: 0x2e51, 0x1947: 0x2e55, 0x1948: 0x1a62, 0x1949: 0x2e59, 0x194a: 0x1a6e, 0x194b: 0x2e5d, - 0x194c: 0x2e61, 0x194d: 0x2e65, 0x1950: 0x2e69, - 0x1952: 0x2e6d, 0x1955: 0x2e71, 0x1956: 0x2e75, 0x1957: 0x2e79, - 0x1958: 0x2e7d, 0x1959: 0x2e81, 0x195a: 0x2e85, 0x195b: 0x2e89, 0x195c: 0x2e8d, 0x195d: 0x2e91, - 0x195e: 0x1a12, 0x1960: 0x2e95, 0x1962: 0x2e99, - 0x1965: 0x2e9d, 0x1966: 0x2ea1, - 0x196a: 0x2ea5, 0x196b: 0x2ea9, 0x196c: 0x2ead, 0x196d: 0x2eb1, - 0x1970: 0x2eb5, 0x1971: 0x2eb9, 0x1972: 0x2ebd, 0x1973: 0x2ec1, 0x1974: 0x2ec5, 0x1975: 0x2ec9, - 0x1976: 0x2ecd, 0x1977: 0x2ed1, 0x1978: 0x2ed5, 0x1979: 0x2ed9, 0x197a: 0x2edd, 0x197b: 0x2ee1, - 0x197c: 0x18d6, 0x197d: 0x2ee5, 0x197e: 0x2ee9, 0x197f: 0x2eed, - // Block 0x66, offset 0x1980 - 0x1980: 0x2ef1, 0x1981: 0x2ef5, 0x1982: 0x2ef9, 0x1983: 0x2efd, 0x1984: 0x2f01, 0x1985: 0x2f05, - 0x1986: 0x2f09, 0x1987: 0x2f0d, 0x1988: 0x2f11, 0x1989: 0x2f15, 0x198a: 0x2f19, 0x198b: 0x2f1d, - 0x198c: 0x2187, 0x198d: 0x2f21, 0x198e: 0x2f25, 0x198f: 0x2f29, 0x1990: 0x2f2d, 0x1991: 0x2197, - 0x1992: 0x2f31, 0x1993: 0x2f35, 0x1994: 0x2f39, 0x1995: 0x2f3d, 0x1996: 0x2f41, 0x1997: 0x2cb1, - 0x1998: 0x2f45, 0x1999: 0x2f49, 0x199a: 0x2f4d, 0x199b: 0x2f51, 0x199c: 0x2f55, 0x199d: 0x2f59, - 0x199e: 0x2f59, 0x199f: 0x2f5d, 0x19a0: 0x2f61, 0x19a1: 0x2f65, 0x19a2: 0x2f69, 0x19a3: 0x2f6d, - 0x19a4: 0x2f71, 0x19a5: 0x2f75, 0x19a6: 0x2f79, 0x19a7: 0x2e9d, 0x19a8: 0x2f7d, 0x19a9: 0x2f81, - 0x19aa: 0x2f85, 0x19ab: 0x2f89, 0x19ac: 0x2f8d, 0x19ad: 0x2f92, - 0x19b0: 0x2f96, 0x19b1: 0x2f9a, 0x19b2: 0x2f9e, 0x19b3: 0x2fa2, 0x19b4: 0x2fa6, 0x19b5: 0x2faa, - 0x19b6: 0x2fae, 0x19b7: 0x2fb2, 0x19b8: 0x2ecd, 0x19b9: 0x2fb6, 0x19ba: 0x2fba, 0x19bb: 0x2fbe, - 0x19bc: 0x2e69, 0x19bd: 0x2fc2, 0x19be: 0x2fc6, 0x19bf: 0x2fca, - // Block 0x67, offset 0x19c0 - 0x19c0: 0x2fce, 0x19c1: 0x2fd2, 0x19c2: 0x2fd6, 0x19c3: 0x2fda, 0x19c4: 0x2fde, 0x19c5: 0x2fe2, - 0x19c6: 0x2fe6, 0x19c7: 0x2fea, 0x19c8: 0x2fee, 0x19c9: 0x2eed, 0x19ca: 0x2ff2, 0x19cb: 0x2ef1, - 0x19cc: 0x2ff6, 0x19cd: 0x2ffa, 0x19ce: 0x2ffe, 0x19cf: 0x3002, 0x19d0: 0x3006, 0x19d1: 0x2e6d, - 0x19d2: 0x2b15, 0x19d3: 0x300a, 0x19d4: 0x300e, 0x19d5: 0x195a, 0x19d6: 0x2c25, 0x19d7: 0x2d71, - 0x19d8: 0x3012, 0x19d9: 0x3016, 0x19da: 0x2f0d, 0x19db: 0x301a, 0x19dc: 0x2f11, 0x19dd: 0x301e, - 0x19de: 0x3022, 0x19df: 0x3026, 0x19e0: 0x2e75, 0x19e1: 0x302a, 0x19e2: 0x302e, 0x19e3: 0x3032, - 0x19e4: 0x3036, 0x19e5: 0x303a, 0x19e6: 0x2e79, 0x19e7: 0x303e, 0x19e8: 0x3042, 0x19e9: 0x3046, - 0x19ea: 0x304a, 0x19eb: 0x304e, 0x19ec: 0x3052, 0x19ed: 0x2f41, 0x19ee: 0x3056, 0x19ef: 0x305a, - 0x19f0: 0x2cb1, 0x19f1: 0x305e, 0x19f2: 0x2f51, 0x19f3: 0x3062, 0x19f4: 0x3066, 0x19f5: 0x306a, - 0x19f6: 0x306e, 0x19f7: 0x3072, 0x19f8: 0x2f65, 0x19f9: 0x3076, 0x19fa: 0x2e99, 0x19fb: 0x307a, - 0x19fc: 0x2f69, 0x19fd: 0x2bd9, 0x19fe: 0x307e, 0x19ff: 0x2f6d, - // Block 0x68, offset 0x1a00 - 0x1a00: 0x3082, 0x1a01: 0x2f75, 0x1a02: 0x3086, 0x1a03: 0x308a, 0x1a04: 0x308e, 0x1a05: 0x3092, - 0x1a06: 0x3096, 0x1a07: 0x2f7d, 0x1a08: 0x2e8d, 0x1a09: 0x309a, 0x1a0a: 0x2f81, 0x1a0b: 0x309e, - 0x1a0c: 0x2f85, 0x1a0d: 0x30a2, 0x1a0e: 0x1b76, 0x1a0f: 0x30a6, 0x1a10: 0x30ab, 0x1a11: 0x30b0, - 0x1a12: 0x30b5, 0x1a13: 0x30b9, 0x1a14: 0x30bd, 0x1a15: 0x30c1, 0x1a16: 0x30c6, 0x1a17: 0x30cb, - 0x1a18: 0x30d0, 0x1a19: 0x30d4, - // Block 0x69, offset 0x1a40 - 0x1a40: 0x30d8, 0x1a41: 0x30db, 0x1a42: 0x30de, 0x1a43: 0x30e1, 0x1a44: 0x30e5, 0x1a45: 0x30e9, - 0x1a46: 0x30e9, - 0x1a53: 0x30ec, 0x1a54: 0x30f1, 0x1a55: 0x30f6, 0x1a56: 0x30fb, 0x1a57: 0x3100, - 0x1a5d: 0x3105, - 0x1a5f: 0x310a, 0x1a60: 0x310f, 0x1a61: 0x14db, 0x1a62: 0x14e4, 0x1a63: 0x3112, - 0x1a64: 0x3115, 0x1a65: 0x3118, 0x1a66: 0x311b, 0x1a67: 0x311e, 0x1a68: 0x3121, 0x1a69: 0x1494, - 0x1a6a: 0x3124, 0x1a6b: 0x3129, 0x1a6c: 0x312e, 0x1a6d: 0x3135, 0x1a6e: 0x313c, 0x1a6f: 0x3141, - 0x1a70: 0x3146, 0x1a71: 0x314b, 0x1a72: 0x3150, 0x1a73: 0x3155, 0x1a74: 0x315a, 0x1a75: 0x315f, - 0x1a76: 0x3164, 0x1a78: 0x3169, 0x1a79: 0x316e, 0x1a7a: 0x3173, 0x1a7b: 0x3178, - 0x1a7c: 0x317d, 0x1a7e: 0x3182, - // Block 0x6a, offset 0x1a80 - 0x1a80: 0x3187, 0x1a81: 0x318c, 0x1a83: 0x3191, 0x1a84: 0x3196, - 0x1a86: 0x319b, 0x1a87: 0x31a0, 0x1a88: 0x31a5, 0x1a89: 0x31aa, 0x1a8a: 0x31af, 0x1a8b: 0x31b4, - 0x1a8c: 0x31b9, 0x1a8d: 0x31be, 0x1a8e: 0x31c3, 0x1a8f: 0x31c8, 0x1a90: 0x31cd, 0x1a91: 0x31cd, - 0x1a92: 0x31d0, 0x1a93: 0x31d0, 0x1a94: 0x31d0, 0x1a95: 0x31d0, 0x1a96: 0x31d3, 0x1a97: 0x31d3, - 0x1a98: 0x31d3, 0x1a99: 0x31d3, 0x1a9a: 0x31d6, 0x1a9b: 0x31d6, 0x1a9c: 0x31d6, 0x1a9d: 0x31d6, - 0x1a9e: 0x31d9, 0x1a9f: 0x31d9, 0x1aa0: 0x31d9, 0x1aa1: 0x31d9, 0x1aa2: 0x31dc, 0x1aa3: 0x31dc, - 0x1aa4: 0x31dc, 0x1aa5: 0x31dc, 0x1aa6: 0x31df, 0x1aa7: 0x31df, 0x1aa8: 0x31df, 0x1aa9: 0x31df, - 0x1aaa: 0x31e2, 0x1aab: 0x31e2, 0x1aac: 0x31e2, 0x1aad: 0x31e2, 0x1aae: 0x31e5, 0x1aaf: 0x31e5, - 0x1ab0: 0x31e5, 0x1ab1: 0x31e5, 0x1ab2: 0x31e8, 0x1ab3: 0x31e8, 0x1ab4: 0x31e8, 0x1ab5: 0x31e8, - 0x1ab6: 0x31eb, 0x1ab7: 0x31eb, 0x1ab8: 0x31eb, 0x1ab9: 0x31eb, 0x1aba: 0x31ee, 0x1abb: 0x31ee, - 0x1abc: 0x31ee, 0x1abd: 0x31ee, 0x1abe: 0x31f1, 0x1abf: 0x31f1, - // Block 0x6b, offset 0x1ac0 - 0x1ac0: 0x31f1, 0x1ac1: 0x31f1, 0x1ac2: 0x31f4, 0x1ac3: 0x31f4, 0x1ac4: 0x31f7, 0x1ac5: 0x31f7, - 0x1ac6: 0x31fa, 0x1ac7: 0x31fa, 0x1ac8: 0x31fd, 0x1ac9: 0x31fd, 0x1aca: 0x3200, 0x1acb: 0x3200, - 0x1acc: 0x3203, 0x1acd: 0x3203, 0x1ace: 0x3206, 0x1acf: 0x3206, 0x1ad0: 0x3206, 0x1ad1: 0x3206, - 0x1ad2: 0x3209, 0x1ad3: 0x3209, 0x1ad4: 0x3209, 0x1ad5: 0x3209, 0x1ad6: 0x320c, 0x1ad7: 0x320c, - 0x1ad8: 0x320c, 0x1ad9: 0x320c, 0x1ada: 0x320f, 0x1adb: 0x320f, 0x1adc: 0x320f, 0x1add: 0x320f, - 0x1ade: 0x3212, 0x1adf: 0x3212, 0x1ae0: 0x3215, 0x1ae1: 0x3215, 0x1ae2: 0x3215, 0x1ae3: 0x3215, - 0x1ae4: 0x06ba, 0x1ae5: 0x06ba, 0x1ae6: 0x3218, 0x1ae7: 0x3218, 0x1ae8: 0x3218, 0x1ae9: 0x3218, - 0x1aea: 0x321b, 0x1aeb: 0x321b, 0x1aec: 0x321b, 0x1aed: 0x321b, 0x1aee: 0x321e, 0x1aef: 0x321e, - 0x1af0: 0x06c4, 0x1af1: 0x06c4, - // Block 0x6c, offset 0x1b00 - 0x1b13: 0x3221, 0x1b14: 0x3221, 0x1b15: 0x3221, 0x1b16: 0x3221, 0x1b17: 0x3224, - 0x1b18: 0x3224, 0x1b19: 0x3227, 0x1b1a: 0x3227, 0x1b1b: 0x322a, 0x1b1c: 0x322a, 0x1b1d: 0x06b0, - 0x1b1e: 0x322d, 0x1b1f: 0x322d, 0x1b20: 0x3230, 0x1b21: 0x3230, 0x1b22: 0x3233, 0x1b23: 0x3233, - 0x1b24: 0x3236, 0x1b25: 0x3236, 0x1b26: 0x3236, 0x1b27: 0x3236, 0x1b28: 0x3239, 0x1b29: 0x3239, - 0x1b2a: 0x323c, 0x1b2b: 0x323c, 0x1b2c: 0x3243, 0x1b2d: 0x3243, 0x1b2e: 0x324a, 0x1b2f: 0x324a, - 0x1b30: 0x3251, 0x1b31: 0x3251, 0x1b32: 0x3258, 0x1b33: 0x3258, 0x1b34: 0x325f, 0x1b35: 0x325f, - 0x1b36: 0x3266, 0x1b37: 0x3266, 0x1b38: 0x3266, 0x1b39: 0x326d, 0x1b3a: 0x326d, 0x1b3b: 0x326d, - 0x1b3c: 0x3274, 0x1b3d: 0x3274, 0x1b3e: 0x3274, 0x1b3f: 0x3274, - // Block 0x6d, offset 0x1b40 - 0x1b40: 0x3277, 0x1b41: 0x327e, 0x1b42: 0x3285, 0x1b43: 0x326d, 0x1b44: 0x328c, 0x1b45: 0x3293, - 0x1b46: 0x3298, 0x1b47: 0x329d, 0x1b48: 0x32a2, 0x1b49: 0x32a7, 0x1b4a: 0x32ac, 0x1b4b: 0x32b1, - 0x1b4c: 0x32b6, 0x1b4d: 0x32bb, 0x1b4e: 0x32c0, 0x1b4f: 0x32c5, 0x1b50: 0x32ca, 0x1b51: 0x32cf, - 0x1b52: 0x32d4, 0x1b53: 0x32d9, 0x1b54: 0x32de, 0x1b55: 0x32e3, 0x1b56: 0x32e8, 0x1b57: 0x32ed, - 0x1b58: 0x32f2, 0x1b59: 0x32f7, 0x1b5a: 0x32fc, 0x1b5b: 0x3301, 0x1b5c: 0x3306, 0x1b5d: 0x330b, - 0x1b5e: 0x3310, 0x1b5f: 0x3315, 0x1b60: 0x331a, 0x1b61: 0x331f, 0x1b62: 0x3324, 0x1b63: 0x3329, - 0x1b64: 0x332e, 0x1b65: 0x3333, 0x1b66: 0x3338, 0x1b67: 0x333d, 0x1b68: 0x3342, 0x1b69: 0x3347, - 0x1b6a: 0x334c, 0x1b6b: 0x3351, 0x1b6c: 0x3356, 0x1b6d: 0x335b, 0x1b6e: 0x3360, 0x1b6f: 0x3365, - 0x1b70: 0x336a, 0x1b71: 0x336f, 0x1b72: 0x3374, 0x1b73: 0x3379, 0x1b74: 0x337e, 0x1b75: 0x3383, - 0x1b76: 0x3388, 0x1b77: 0x338d, 0x1b78: 0x3392, 0x1b79: 0x3397, 0x1b7a: 0x339c, 0x1b7b: 0x33a1, - 0x1b7c: 0x33a6, 0x1b7d: 0x33ab, 0x1b7e: 0x33b0, 0x1b7f: 0x33b5, - // Block 0x6e, offset 0x1b80 - 0x1b80: 0x33ba, 0x1b81: 0x33bf, 0x1b82: 0x33c4, 0x1b83: 0x33c9, 0x1b84: 0x33ce, 0x1b85: 0x33d3, - 0x1b86: 0x33d8, 0x1b87: 0x33dd, 0x1b88: 0x33e2, 0x1b89: 0x33e7, 0x1b8a: 0x33ec, 0x1b8b: 0x33f1, - 0x1b8c: 0x33f6, 0x1b8d: 0x33fb, 0x1b8e: 0x3400, 0x1b8f: 0x3405, 0x1b90: 0x340a, 0x1b91: 0x340f, - 0x1b92: 0x3414, 0x1b93: 0x3419, 0x1b94: 0x341e, 0x1b95: 0x3423, 0x1b96: 0x3428, 0x1b97: 0x342d, - 0x1b98: 0x3432, 0x1b99: 0x3437, 0x1b9a: 0x343c, 0x1b9b: 0x3441, 0x1b9c: 0x3446, 0x1b9d: 0x344b, - 0x1b9e: 0x3450, 0x1b9f: 0x3456, 0x1ba0: 0x345c, 0x1ba1: 0x3462, 0x1ba2: 0x3468, 0x1ba3: 0x346e, - 0x1ba4: 0x3474, 0x1ba5: 0x347b, 0x1ba6: 0x3285, 0x1ba7: 0x3482, 0x1ba8: 0x326d, 0x1ba9: 0x328c, - 0x1baa: 0x3489, 0x1bab: 0x348e, 0x1bac: 0x32a2, 0x1bad: 0x3493, 0x1bae: 0x32a7, 0x1baf: 0x32ac, - 0x1bb0: 0x3498, 0x1bb1: 0x349d, 0x1bb2: 0x32c0, 0x1bb3: 0x34a2, 0x1bb4: 0x32c5, 0x1bb5: 0x32ca, - 0x1bb6: 0x34a7, 0x1bb7: 0x34ac, 0x1bb8: 0x32d4, 0x1bb9: 0x34b1, 0x1bba: 0x32d9, 0x1bbb: 0x32de, - 0x1bbc: 0x336f, 0x1bbd: 0x3374, 0x1bbe: 0x3383, 0x1bbf: 0x3388, - // Block 0x6f, offset 0x1bc0 - 0x1bc0: 0x338d, 0x1bc1: 0x33a1, 0x1bc2: 0x33a6, 0x1bc3: 0x33ab, 0x1bc4: 0x33b0, 0x1bc5: 0x33c4, - 0x1bc6: 0x33c9, 0x1bc7: 0x33ce, 0x1bc8: 0x34b6, 0x1bc9: 0x33e2, 0x1bca: 0x34bb, 0x1bcb: 0x34c0, - 0x1bcc: 0x3400, 0x1bcd: 0x34c5, 0x1bce: 0x3405, 0x1bcf: 0x340a, 0x1bd0: 0x344b, 0x1bd1: 0x34ca, - 0x1bd2: 0x34cf, 0x1bd3: 0x3432, 0x1bd4: 0x34d4, 0x1bd5: 0x3437, 0x1bd6: 0x343c, 0x1bd7: 0x3277, - 0x1bd8: 0x327e, 0x1bd9: 0x34d9, 0x1bda: 0x3285, 0x1bdb: 0x34e0, 0x1bdc: 0x3293, 0x1bdd: 0x3298, - 0x1bde: 0x329d, 0x1bdf: 0x32a2, 0x1be0: 0x34e7, 0x1be1: 0x32b1, 0x1be2: 0x32b6, 0x1be3: 0x32bb, - 0x1be4: 0x32c0, 0x1be5: 0x34ec, 0x1be6: 0x32d4, 0x1be7: 0x32e3, 0x1be8: 0x32e8, 0x1be9: 0x32ed, - 0x1bea: 0x32f2, 0x1beb: 0x32f7, 0x1bec: 0x3301, 0x1bed: 0x3306, 0x1bee: 0x330b, 0x1bef: 0x3310, - 0x1bf0: 0x3315, 0x1bf1: 0x331a, 0x1bf2: 0x34f1, 0x1bf3: 0x331f, 0x1bf4: 0x3324, 0x1bf5: 0x3329, - 0x1bf6: 0x332e, 0x1bf7: 0x3333, 0x1bf8: 0x3338, 0x1bf9: 0x3342, 0x1bfa: 0x3347, 0x1bfb: 0x334c, - 0x1bfc: 0x3351, 0x1bfd: 0x3356, 0x1bfe: 0x335b, 0x1bff: 0x3360, - // Block 0x70, offset 0x1c00 - 0x1c00: 0x3365, 0x1c01: 0x336a, 0x1c02: 0x3379, 0x1c03: 0x337e, 0x1c04: 0x3392, 0x1c05: 0x3397, - 0x1c06: 0x339c, 0x1c07: 0x33a1, 0x1c08: 0x33a6, 0x1c09: 0x33b5, 0x1c0a: 0x33ba, 0x1c0b: 0x33bf, - 0x1c0c: 0x33c4, 0x1c0d: 0x34f6, 0x1c0e: 0x33d3, 0x1c0f: 0x33d8, 0x1c10: 0x33dd, 0x1c11: 0x33e2, - 0x1c12: 0x33f1, 0x1c13: 0x33f6, 0x1c14: 0x33fb, 0x1c15: 0x3400, 0x1c16: 0x34fb, 0x1c17: 0x340f, - 0x1c18: 0x3414, 0x1c19: 0x3500, 0x1c1a: 0x3423, 0x1c1b: 0x3428, 0x1c1c: 0x342d, 0x1c1d: 0x3432, - 0x1c1e: 0x3505, 0x1c1f: 0x3285, 0x1c20: 0x34e0, 0x1c21: 0x32a2, 0x1c22: 0x34e7, 0x1c23: 0x32c0, - 0x1c24: 0x34ec, 0x1c25: 0x32d4, 0x1c26: 0x350a, 0x1c27: 0x3315, 0x1c28: 0x350f, 0x1c29: 0x3514, - 0x1c2a: 0x3519, 0x1c2b: 0x33a1, 0x1c2c: 0x33a6, 0x1c2d: 0x33c4, 0x1c2e: 0x3400, 0x1c2f: 0x34fb, - 0x1c30: 0x3432, 0x1c31: 0x3505, 0x1c32: 0x351e, 0x1c33: 0x3525, 0x1c34: 0x352c, 0x1c35: 0x3533, - 0x1c36: 0x3538, 0x1c37: 0x353d, 0x1c38: 0x3542, 0x1c39: 0x3547, 0x1c3a: 0x354c, 0x1c3b: 0x3551, - 0x1c3c: 0x3556, 0x1c3d: 0x355b, 0x1c3e: 0x3560, 0x1c3f: 0x3565, - // Block 0x71, offset 0x1c40 - 0x1c40: 0x356a, 0x1c41: 0x356f, 0x1c42: 0x3574, 0x1c43: 0x3579, 0x1c44: 0x357e, 0x1c45: 0x3583, - 0x1c46: 0x3588, 0x1c47: 0x358d, 0x1c48: 0x3592, 0x1c49: 0x3597, 0x1c4a: 0x359c, 0x1c4b: 0x35a1, - 0x1c4c: 0x3514, 0x1c4d: 0x35a6, 0x1c4e: 0x35ab, 0x1c4f: 0x35b0, 0x1c50: 0x35b5, 0x1c51: 0x3533, - 0x1c52: 0x3538, 0x1c53: 0x353d, 0x1c54: 0x3542, 0x1c55: 0x3547, 0x1c56: 0x354c, 0x1c57: 0x3551, - 0x1c58: 0x3556, 0x1c59: 0x355b, 0x1c5a: 0x3560, 0x1c5b: 0x3565, 0x1c5c: 0x356a, 0x1c5d: 0x356f, - 0x1c5e: 0x3574, 0x1c5f: 0x3579, 0x1c60: 0x357e, 0x1c61: 0x3583, 0x1c62: 0x3588, 0x1c63: 0x358d, - 0x1c64: 0x3592, 0x1c65: 0x3597, 0x1c66: 0x359c, 0x1c67: 0x35a1, 0x1c68: 0x3514, 0x1c69: 0x35a6, - 0x1c6a: 0x35ab, 0x1c6b: 0x35b0, 0x1c6c: 0x35b5, 0x1c6d: 0x3597, 0x1c6e: 0x359c, 0x1c6f: 0x35a1, - 0x1c70: 0x3514, 0x1c71: 0x350f, 0x1c72: 0x3519, 0x1c73: 0x333d, 0x1c74: 0x3306, 0x1c75: 0x330b, - 0x1c76: 0x3310, 0x1c77: 0x3597, 0x1c78: 0x359c, 0x1c79: 0x35a1, 0x1c7a: 0x333d, 0x1c7b: 0x3342, - 0x1c7c: 0x35ba, 0x1c7d: 0x35ba, - // Block 0x72, offset 0x1c80 - 0x1c90: 0x35bf, 0x1c91: 0x35c6, - 0x1c92: 0x35c6, 0x1c93: 0x35cd, 0x1c94: 0x35d4, 0x1c95: 0x35db, 0x1c96: 0x35e2, 0x1c97: 0x35e9, - 0x1c98: 0x35f0, 0x1c99: 0x35f0, 0x1c9a: 0x35f7, 0x1c9b: 0x35fe, 0x1c9c: 0x3605, 0x1c9d: 0x360c, - 0x1c9e: 0x3613, 0x1c9f: 0x361a, 0x1ca0: 0x361a, 0x1ca1: 0x3621, 0x1ca2: 0x3628, 0x1ca3: 0x3628, - 0x1ca4: 0x362f, 0x1ca5: 0x362f, 0x1ca6: 0x3636, 0x1ca7: 0x363d, 0x1ca8: 0x363d, 0x1ca9: 0x3644, - 0x1caa: 0x364b, 0x1cab: 0x364b, 0x1cac: 0x3652, 0x1cad: 0x3652, 0x1cae: 0x3659, 0x1caf: 0x3660, - 0x1cb0: 0x3660, 0x1cb1: 0x3667, 0x1cb2: 0x3667, 0x1cb3: 0x366e, 0x1cb4: 0x3675, 0x1cb5: 0x367c, - 0x1cb6: 0x3683, 0x1cb7: 0x3683, 0x1cb8: 0x368a, 0x1cb9: 0x3691, 0x1cba: 0x3698, 0x1cbb: 0x369f, - 0x1cbc: 0x36a6, 0x1cbd: 0x36a6, 0x1cbe: 0x36ad, 0x1cbf: 0x36b4, - // Block 0x73, offset 0x1cc0 - 0x1cc0: 0x36bb, 0x1cc1: 0x36c2, 0x1cc2: 0x36c9, 0x1cc3: 0x36d0, 0x1cc4: 0x36d0, 0x1cc5: 0x36d7, - 0x1cc6: 0x36d7, 0x1cc7: 0x36de, 0x1cc8: 0x36de, 0x1cc9: 0x36e5, 0x1cca: 0x36ec, 0x1ccb: 0x36f3, - 0x1ccc: 0x36fa, 0x1ccd: 0x3701, 0x1cce: 0x3708, 0x1ccf: 0x370f, - 0x1cd2: 0x3716, 0x1cd3: 0x371d, 0x1cd4: 0x3724, 0x1cd5: 0x372b, 0x1cd6: 0x3732, 0x1cd7: 0x3739, - 0x1cd8: 0x3739, 0x1cd9: 0x3740, 0x1cda: 0x3747, 0x1cdb: 0x374e, 0x1cdc: 0x3755, 0x1cdd: 0x3755, - 0x1cde: 0x375c, 0x1cdf: 0x3763, 0x1ce0: 0x376a, 0x1ce1: 0x3771, 0x1ce2: 0x3778, 0x1ce3: 0x377f, - 0x1ce4: 0x3786, 0x1ce5: 0x378d, 0x1ce6: 0x3794, 0x1ce7: 0x379b, 0x1ce8: 0x37a2, 0x1ce9: 0x37a9, - 0x1cea: 0x37b0, 0x1ceb: 0x37b7, 0x1cec: 0x37be, 0x1ced: 0x37c5, 0x1cee: 0x37cc, 0x1cef: 0x37d3, - 0x1cf0: 0x37da, 0x1cf1: 0x37e1, 0x1cf2: 0x37e8, 0x1cf3: 0x37ef, 0x1cf4: 0x36ad, 0x1cf5: 0x36bb, - 0x1cf6: 0x37f6, 0x1cf7: 0x37fd, 0x1cf8: 0x3804, 0x1cf9: 0x380b, 0x1cfa: 0x3812, 0x1cfb: 0x3819, - 0x1cfc: 0x3812, 0x1cfd: 0x3804, 0x1cfe: 0x3820, 0x1cff: 0x3827, - // Block 0x74, offset 0x1d00 - 0x1d00: 0x382e, 0x1d01: 0x3835, 0x1d02: 0x383c, 0x1d03: 0x3819, 0x1d04: 0x367c, 0x1d05: 0x3636, - 0x1d06: 0x3843, 0x1d07: 0x384a, - 0x1d30: 0x3851, 0x1d31: 0x3858, 0x1d32: 0x385f, 0x1d33: 0x3868, 0x1d34: 0x3871, 0x1d35: 0x387a, - 0x1d36: 0x3883, 0x1d37: 0x388c, 0x1d38: 0x3895, 0x1d39: 0x389e, 0x1d3a: 0x38a5, 0x1d3b: 0x38c7, - 0x1d3c: 0x38d7, - // Block 0x75, offset 0x1d40 - 0x1d50: 0x38e0, 0x1d51: 0x38e2, - 0x1d52: 0x38e6, 0x1d53: 0x38ea, 0x1d54: 0x04e1, 0x1d55: 0x38ec, 0x1d56: 0x38ee, 0x1d57: 0x38f0, - 0x1d58: 0x38f4, 0x1d59: 0x1443, - 0x1d70: 0x1440, 0x1d71: 0x38f8, 0x1d72: 0x38fc, 0x1d73: 0x3900, 0x1d74: 0x3900, 0x1d75: 0x149c, - 0x1d76: 0x149e, 0x1d77: 0x3902, 0x1d78: 0x3904, 0x1d79: 0x3906, 0x1d7a: 0x390a, 0x1d7b: 0x390e, - 0x1d7c: 0x3912, 0x1d7d: 0x3916, 0x1d7e: 0x391a, 0x1d7f: 0x16c3, - // Block 0x76, offset 0x1d80 - 0x1d80: 0x16c7, 0x1d81: 0x391e, 0x1d82: 0x3922, 0x1d83: 0x3926, 0x1d84: 0x392a, - 0x1d87: 0x392e, 0x1d88: 0x3930, 0x1d89: 0x146c, 0x1d8a: 0x146c, 0x1d8b: 0x146c, - 0x1d8c: 0x146c, 0x1d8d: 0x3900, 0x1d8e: 0x3900, 0x1d8f: 0x3900, 0x1d90: 0x38e0, 0x1d91: 0x38e2, - 0x1d92: 0x143e, 0x1d94: 0x04e1, 0x1d95: 0x38ea, 0x1d96: 0x38ee, 0x1d97: 0x38ec, - 0x1d98: 0x38f8, 0x1d99: 0x149c, 0x1d9a: 0x149e, 0x1d9b: 0x3902, 0x1d9c: 0x3904, 0x1d9d: 0x3906, - 0x1d9e: 0x390a, 0x1d9f: 0x3932, 0x1da0: 0x3934, 0x1da1: 0x3936, 0x1da2: 0x1494, 0x1da3: 0x3938, - 0x1da4: 0x393a, 0x1da5: 0x393c, 0x1da6: 0x149a, 0x1da8: 0x393e, 0x1da9: 0x3940, - 0x1daa: 0x3942, 0x1dab: 0x3944, - 0x1db0: 0x3946, 0x1db1: 0x394a, 0x1db2: 0x394f, 0x1db4: 0x3953, - 0x1db6: 0x3957, 0x1db7: 0x395b, 0x1db8: 0x3960, 0x1db9: 0x3964, 0x1dba: 0x3969, 0x1dbb: 0x396d, - 0x1dbc: 0x3972, 0x1dbd: 0x3976, 0x1dbe: 0x397b, 0x1dbf: 0x397f, - // Block 0x77, offset 0x1dc0 - 0x1dc0: 0x3984, 0x1dc1: 0x068d, 0x1dc2: 0x068d, 0x1dc3: 0x0692, 0x1dc4: 0x0692, 0x1dc5: 0x0697, - 0x1dc6: 0x0697, 0x1dc7: 0x069c, 0x1dc8: 0x069c, 0x1dc9: 0x06a1, 0x1dca: 0x06a1, 0x1dcb: 0x06a1, - 0x1dcc: 0x06a1, 0x1dcd: 0x3987, 0x1dce: 0x3987, 0x1dcf: 0x398a, 0x1dd0: 0x398a, 0x1dd1: 0x398a, - 0x1dd2: 0x398a, 0x1dd3: 0x398d, 0x1dd4: 0x398d, 0x1dd5: 0x3990, 0x1dd6: 0x3990, 0x1dd7: 0x3990, - 0x1dd8: 0x3990, 0x1dd9: 0x3993, 0x1dda: 0x3993, 0x1ddb: 0x3993, 0x1ddc: 0x3993, 0x1ddd: 0x3996, - 0x1dde: 0x3996, 0x1ddf: 0x3996, 0x1de0: 0x3996, 0x1de1: 0x3999, 0x1de2: 0x3999, 0x1de3: 0x3999, - 0x1de4: 0x3999, 0x1de5: 0x399c, 0x1de6: 0x399c, 0x1de7: 0x399c, 0x1de8: 0x399c, 0x1de9: 0x399f, - 0x1dea: 0x399f, 0x1deb: 0x39a2, 0x1dec: 0x39a2, 0x1ded: 0x39a5, 0x1dee: 0x39a5, 0x1def: 0x39a8, - 0x1df0: 0x39a8, 0x1df1: 0x39ab, 0x1df2: 0x39ab, 0x1df3: 0x39ab, 0x1df4: 0x39ab, 0x1df5: 0x39ae, - 0x1df6: 0x39ae, 0x1df7: 0x39ae, 0x1df8: 0x39ae, 0x1df9: 0x39b1, 0x1dfa: 0x39b1, 0x1dfb: 0x39b1, - 0x1dfc: 0x39b1, 0x1dfd: 0x39b4, 0x1dfe: 0x39b4, 0x1dff: 0x39b4, - // Block 0x78, offset 0x1e00 - 0x1e00: 0x39b4, 0x1e01: 0x39b7, 0x1e02: 0x39b7, 0x1e03: 0x39b7, 0x1e04: 0x39b7, 0x1e05: 0x39ba, - 0x1e06: 0x39ba, 0x1e07: 0x39ba, 0x1e08: 0x39ba, 0x1e09: 0x39bd, 0x1e0a: 0x39bd, 0x1e0b: 0x39bd, - 0x1e0c: 0x39bd, 0x1e0d: 0x39c0, 0x1e0e: 0x39c0, 0x1e0f: 0x39c0, 0x1e10: 0x39c0, 0x1e11: 0x39c3, - 0x1e12: 0x39c3, 0x1e13: 0x39c3, 0x1e14: 0x39c3, 0x1e15: 0x39c6, 0x1e16: 0x39c6, 0x1e17: 0x39c6, - 0x1e18: 0x39c6, 0x1e19: 0x39c9, 0x1e1a: 0x39c9, 0x1e1b: 0x39c9, 0x1e1c: 0x39c9, 0x1e1d: 0x39cc, - 0x1e1e: 0x39cc, 0x1e1f: 0x39cc, 0x1e20: 0x39cc, 0x1e21: 0x39cf, 0x1e22: 0x39cf, 0x1e23: 0x39cf, - 0x1e24: 0x39cf, 0x1e25: 0x39d2, 0x1e26: 0x39d2, 0x1e27: 0x39d2, 0x1e28: 0x39d2, 0x1e29: 0x39d5, - 0x1e2a: 0x39d5, 0x1e2b: 0x39d5, 0x1e2c: 0x39d5, 0x1e2d: 0x39d8, 0x1e2e: 0x39d8, 0x1e2f: 0x3239, - 0x1e30: 0x3239, 0x1e31: 0x39db, 0x1e32: 0x39db, 0x1e33: 0x39db, 0x1e34: 0x39db, 0x1e35: 0x39de, - 0x1e36: 0x39de, 0x1e37: 0x39e5, 0x1e38: 0x39e5, 0x1e39: 0x39ec, 0x1e3a: 0x39ec, 0x1e3b: 0x39f3, - 0x1e3c: 0x39f3, - // Block 0x79, offset 0x1e40 - 0x1e41: 0x38ec, 0x1e42: 0x39f8, 0x1e43: 0x3932, 0x1e44: 0x3940, 0x1e45: 0x3942, - 0x1e46: 0x3934, 0x1e47: 0x39fa, 0x1e48: 0x149c, 0x1e49: 0x149e, 0x1e4a: 0x3936, 0x1e4b: 0x1494, - 0x1e4c: 0x38e0, 0x1e4d: 0x3938, 0x1e4e: 0x143e, 0x1e4f: 0x39fc, 0x1e50: 0x1486, 0x1e51: 0x001c, - 0x1e52: 0x000d, 0x1e53: 0x000f, 0x1e54: 0x1488, 0x1e55: 0x148a, 0x1e56: 0x148c, 0x1e57: 0x148e, - 0x1e58: 0x1490, 0x1e59: 0x1492, 0x1e5a: 0x38ea, 0x1e5b: 0x04e1, 0x1e5c: 0x393a, 0x1e5d: 0x149a, - 0x1e5e: 0x393c, 0x1e5f: 0x38ee, 0x1e60: 0x3944, 0x1e61: 0x0906, 0x1e62: 0x090b, 0x1e63: 0x14ad, - 0x1e64: 0x090d, 0x1e65: 0x090f, 0x1e66: 0x14d9, 0x1e67: 0x0914, 0x1e68: 0x0916, 0x1e69: 0x0918, - 0x1e6a: 0x091a, 0x1e6b: 0x091c, 0x1e6c: 0x091e, 0x1e6d: 0x0920, 0x1e6e: 0x0922, 0x1e6f: 0x0924, - 0x1e70: 0x0929, 0x1e71: 0x14c8, 0x1e72: 0x092b, 0x1e73: 0x17f6, 0x1e74: 0x092d, 0x1e75: 0x092f, - 0x1e76: 0x155f, 0x1e77: 0x0931, 0x1e78: 0x1570, 0x1e79: 0x17f8, 0x1e7a: 0x14d4, 0x1e7b: 0x392e, - 0x1e7c: 0x393e, 0x1e7d: 0x3930, 0x1e7e: 0x39fe, 0x1e7f: 0x3900, - // Block 0x7a, offset 0x1e80 - 0x1e80: 0x13f7, 0x1e81: 0x0007, 0x1e82: 0x093d, 0x1e83: 0x0984, 0x1e84: 0x093f, 0x1e85: 0x0941, - 0x1e86: 0x098c, 0x1e87: 0x094c, 0x1e88: 0x0494, 0x1e89: 0x097c, 0x1e8a: 0x0499, 0x1e8b: 0x094e, - 0x1e8c: 0x04c5, 0x1e8d: 0x0950, 0x1e8e: 0x14a0, 0x1e8f: 0x001e, 0x1e90: 0x0960, 0x1e91: 0x17fa, - 0x1e92: 0x049b, 0x1e93: 0x02c8, 0x1e94: 0x0962, 0x1e95: 0x0964, 0x1e96: 0x096d, 0x1e97: 0x04a6, - 0x1e98: 0x04c7, 0x1e99: 0x04a8, 0x1e9a: 0x09df, 0x1e9b: 0x3902, 0x1e9c: 0x3a00, 0x1e9d: 0x3904, - 0x1e9e: 0x3a02, 0x1e9f: 0x3a04, 0x1ea0: 0x3a08, 0x1ea1: 0x38e6, 0x1ea2: 0x391e, 0x1ea3: 0x3922, - 0x1ea4: 0x38e2, 0x1ea5: 0x3a0c, 0x1ea6: 0x2321, 0x1ea7: 0x3a10, 0x1ea8: 0x3a14, 0x1ea9: 0x3a18, - 0x1eaa: 0x3a1c, 0x1eab: 0x3a20, 0x1eac: 0x3a24, 0x1ead: 0x3a28, 0x1eae: 0x3a2c, 0x1eaf: 0x3a30, - 0x1eb0: 0x3a34, 0x1eb1: 0x2269, 0x1eb2: 0x226d, 0x1eb3: 0x2271, 0x1eb4: 0x2275, 0x1eb5: 0x2279, - 0x1eb6: 0x227d, 0x1eb7: 0x2281, 0x1eb8: 0x2285, 0x1eb9: 0x2289, 0x1eba: 0x228d, 0x1ebb: 0x2291, - 0x1ebc: 0x2295, 0x1ebd: 0x2299, 0x1ebe: 0x229d, 0x1ebf: 0x22a1, - // Block 0x7b, offset 0x1ec0 - 0x1ec0: 0x22a5, 0x1ec1: 0x22a9, 0x1ec2: 0x22ad, 0x1ec3: 0x22b1, 0x1ec4: 0x22b5, 0x1ec5: 0x22b9, - 0x1ec6: 0x22bd, 0x1ec7: 0x22c1, 0x1ec8: 0x22c5, 0x1ec9: 0x22c9, 0x1eca: 0x22cd, 0x1ecb: 0x22d1, - 0x1ecc: 0x22d5, 0x1ecd: 0x22d9, 0x1ece: 0x22dd, 0x1ecf: 0x22e1, 0x1ed0: 0x22e5, 0x1ed1: 0x22e9, - 0x1ed2: 0x22ed, 0x1ed3: 0x22f1, 0x1ed4: 0x22f5, 0x1ed5: 0x22f9, 0x1ed6: 0x22fd, 0x1ed7: 0x2301, - 0x1ed8: 0x2305, 0x1ed9: 0x2309, 0x1eda: 0x230d, 0x1edb: 0x2311, 0x1edc: 0x2315, 0x1edd: 0x3a38, - 0x1ede: 0x3a3c, 0x1edf: 0x3a40, 0x1ee0: 0x1e04, 0x1ee1: 0x1d38, 0x1ee2: 0x1d3c, 0x1ee3: 0x1d40, - 0x1ee4: 0x1d44, 0x1ee5: 0x1d48, 0x1ee6: 0x1d4c, 0x1ee7: 0x1d50, 0x1ee8: 0x1d54, 0x1ee9: 0x1d58, - 0x1eea: 0x1d5c, 0x1eeb: 0x1d60, 0x1eec: 0x1d64, 0x1eed: 0x1d68, 0x1eee: 0x1d6c, 0x1eef: 0x1d70, - 0x1ef0: 0x1d74, 0x1ef1: 0x1d78, 0x1ef2: 0x1d7c, 0x1ef3: 0x1d80, 0x1ef4: 0x1d84, 0x1ef5: 0x1d88, - 0x1ef6: 0x1d8c, 0x1ef7: 0x1d90, 0x1ef8: 0x1d94, 0x1ef9: 0x1d98, 0x1efa: 0x1d9c, 0x1efb: 0x1da0, - 0x1efc: 0x1da4, 0x1efd: 0x1da8, 0x1efe: 0x1dac, - // Block 0x7c, offset 0x1f00 - 0x1f02: 0x1db0, 0x1f03: 0x1db4, 0x1f04: 0x1db8, 0x1f05: 0x1dbc, - 0x1f06: 0x1dc0, 0x1f07: 0x1dc4, 0x1f0a: 0x1dc8, 0x1f0b: 0x1dcc, - 0x1f0c: 0x1dd0, 0x1f0d: 0x1dd4, 0x1f0e: 0x1dd8, 0x1f0f: 0x1ddc, - 0x1f12: 0x1de0, 0x1f13: 0x1de4, 0x1f14: 0x1de8, 0x1f15: 0x1dec, 0x1f16: 0x1df0, 0x1f17: 0x1df4, - 0x1f1a: 0x1df8, 0x1f1b: 0x1dfc, 0x1f1c: 0x1e00, - 0x1f20: 0x3a44, 0x1f21: 0x3a47, 0x1f22: 0x3a4a, 0x1f23: 0x0009, - 0x1f24: 0x3a4d, 0x1f25: 0x3a50, 0x1f26: 0x3a53, 0x1f28: 0x3a57, 0x1f29: 0x3a5b, - 0x1f2a: 0x3a5f, 0x1f2b: 0x3a63, 0x1f2c: 0x3a67, 0x1f2d: 0x3a6b, 0x1f2e: 0x3a6f, - // Block 0x7d, offset 0x1f40 - 0x1f5a: 0x3a73, 0x1f5c: 0x3a7c, - 0x1f6b: 0x3a85, - // Block 0x7e, offset 0x1f80 - 0x1f9e: 0x3a8e, 0x1f9f: 0x3a97, 0x1fa0: 0x3aa0, 0x1fa1: 0x3aad, 0x1fa2: 0x3aba, 0x1fa3: 0x3ac7, - 0x1fa4: 0x3ad4, - // Block 0x7f, offset 0x1fc0 - 0x1ffb: 0x3ae1, - 0x1ffc: 0x3aea, 0x1ffd: 0x3af3, 0x1ffe: 0x3b00, 0x1fff: 0x3b0d, - // Block 0x80, offset 0x2000 - 0x2000: 0x3b1a, - // Block 0x81, offset 0x2040 - 0x2040: 0x0906, 0x2041: 0x090b, 0x2042: 0x14ad, 0x2043: 0x090d, 0x2044: 0x090f, 0x2045: 0x14d9, - 0x2046: 0x0914, 0x2047: 0x0916, 0x2048: 0x0918, 0x2049: 0x091a, 0x204a: 0x091c, 0x204b: 0x091e, - 0x204c: 0x0920, 0x204d: 0x0922, 0x204e: 0x0924, 0x204f: 0x0929, 0x2050: 0x14c8, 0x2051: 0x092b, - 0x2052: 0x17f6, 0x2053: 0x092d, 0x2054: 0x092f, 0x2055: 0x155f, 0x2056: 0x0931, 0x2057: 0x1570, - 0x2058: 0x17f8, 0x2059: 0x14d4, 0x205a: 0x0007, 0x205b: 0x093d, 0x205c: 0x0984, 0x205d: 0x093f, - 0x205e: 0x0941, 0x205f: 0x098c, 0x2060: 0x094c, 0x2061: 0x0494, 0x2062: 0x097c, 0x2063: 0x0499, - 0x2064: 0x094e, 0x2065: 0x04c5, 0x2066: 0x0950, 0x2067: 0x14a0, 0x2068: 0x001e, 0x2069: 0x0960, - 0x206a: 0x17fa, 0x206b: 0x049b, 0x206c: 0x02c8, 0x206d: 0x0962, 0x206e: 0x0964, 0x206f: 0x096d, - 0x2070: 0x04a6, 0x2071: 0x04c7, 0x2072: 0x04a8, 0x2073: 0x09df, 0x2074: 0x0906, 0x2075: 0x090b, - 0x2076: 0x14ad, 0x2077: 0x090d, 0x2078: 0x090f, 0x2079: 0x14d9, 0x207a: 0x0914, 0x207b: 0x0916, - 0x207c: 0x0918, 0x207d: 0x091a, 0x207e: 0x091c, 0x207f: 0x091e, - // Block 0x82, offset 0x2080 - 0x2080: 0x0920, 0x2081: 0x0922, 0x2082: 0x0924, 0x2083: 0x0929, 0x2084: 0x14c8, 0x2085: 0x092b, - 0x2086: 0x17f6, 0x2087: 0x092d, 0x2088: 0x092f, 0x2089: 0x155f, 0x208a: 0x0931, 0x208b: 0x1570, - 0x208c: 0x17f8, 0x208d: 0x14d4, 0x208e: 0x0007, 0x208f: 0x093d, 0x2090: 0x0984, 0x2091: 0x093f, - 0x2092: 0x0941, 0x2093: 0x098c, 0x2094: 0x094c, 0x2096: 0x097c, 0x2097: 0x0499, - 0x2098: 0x094e, 0x2099: 0x04c5, 0x209a: 0x0950, 0x209b: 0x14a0, 0x209c: 0x001e, 0x209d: 0x0960, - 0x209e: 0x17fa, 0x209f: 0x049b, 0x20a0: 0x02c8, 0x20a1: 0x0962, 0x20a2: 0x0964, 0x20a3: 0x096d, - 0x20a4: 0x04a6, 0x20a5: 0x04c7, 0x20a6: 0x04a8, 0x20a7: 0x09df, 0x20a8: 0x0906, 0x20a9: 0x090b, - 0x20aa: 0x14ad, 0x20ab: 0x090d, 0x20ac: 0x090f, 0x20ad: 0x14d9, 0x20ae: 0x0914, 0x20af: 0x0916, - 0x20b0: 0x0918, 0x20b1: 0x091a, 0x20b2: 0x091c, 0x20b3: 0x091e, 0x20b4: 0x0920, 0x20b5: 0x0922, - 0x20b6: 0x0924, 0x20b7: 0x0929, 0x20b8: 0x14c8, 0x20b9: 0x092b, 0x20ba: 0x17f6, 0x20bb: 0x092d, - 0x20bc: 0x092f, 0x20bd: 0x155f, 0x20be: 0x0931, 0x20bf: 0x1570, - // Block 0x83, offset 0x20c0 - 0x20c0: 0x17f8, 0x20c1: 0x14d4, 0x20c2: 0x0007, 0x20c3: 0x093d, 0x20c4: 0x0984, 0x20c5: 0x093f, - 0x20c6: 0x0941, 0x20c7: 0x098c, 0x20c8: 0x094c, 0x20c9: 0x0494, 0x20ca: 0x097c, 0x20cb: 0x0499, - 0x20cc: 0x094e, 0x20cd: 0x04c5, 0x20ce: 0x0950, 0x20cf: 0x14a0, 0x20d0: 0x001e, 0x20d1: 0x0960, - 0x20d2: 0x17fa, 0x20d3: 0x049b, 0x20d4: 0x02c8, 0x20d5: 0x0962, 0x20d6: 0x0964, 0x20d7: 0x096d, - 0x20d8: 0x04a6, 0x20d9: 0x04c7, 0x20da: 0x04a8, 0x20db: 0x09df, 0x20dc: 0x0906, - 0x20de: 0x14ad, 0x20df: 0x090d, 0x20e2: 0x0914, - 0x20e5: 0x091a, 0x20e6: 0x091c, 0x20e9: 0x0922, - 0x20ea: 0x0924, 0x20eb: 0x0929, 0x20ec: 0x14c8, 0x20ee: 0x17f6, 0x20ef: 0x092d, - 0x20f0: 0x092f, 0x20f1: 0x155f, 0x20f2: 0x0931, 0x20f3: 0x1570, 0x20f4: 0x17f8, 0x20f5: 0x14d4, - 0x20f6: 0x0007, 0x20f7: 0x093d, 0x20f8: 0x0984, 0x20f9: 0x093f, 0x20fb: 0x098c, - 0x20fd: 0x0494, 0x20fe: 0x097c, 0x20ff: 0x0499, - // Block 0x84, offset 0x2100 - 0x2100: 0x094e, 0x2101: 0x04c5, 0x2102: 0x0950, 0x2103: 0x14a0, 0x2105: 0x0960, - 0x2106: 0x17fa, 0x2107: 0x049b, 0x2108: 0x02c8, 0x2109: 0x0962, 0x210a: 0x0964, 0x210b: 0x096d, - 0x210c: 0x04a6, 0x210d: 0x04c7, 0x210e: 0x04a8, 0x210f: 0x09df, 0x2110: 0x0906, 0x2111: 0x090b, - 0x2112: 0x14ad, 0x2113: 0x090d, 0x2114: 0x090f, 0x2115: 0x14d9, 0x2116: 0x0914, 0x2117: 0x0916, - 0x2118: 0x0918, 0x2119: 0x091a, 0x211a: 0x091c, 0x211b: 0x091e, 0x211c: 0x0920, 0x211d: 0x0922, - 0x211e: 0x0924, 0x211f: 0x0929, 0x2120: 0x14c8, 0x2121: 0x092b, 0x2122: 0x17f6, 0x2123: 0x092d, - 0x2124: 0x092f, 0x2125: 0x155f, 0x2126: 0x0931, 0x2127: 0x1570, 0x2128: 0x17f8, 0x2129: 0x14d4, - 0x212a: 0x0007, 0x212b: 0x093d, 0x212c: 0x0984, 0x212d: 0x093f, 0x212e: 0x0941, 0x212f: 0x098c, - 0x2130: 0x094c, 0x2131: 0x0494, 0x2132: 0x097c, 0x2133: 0x0499, 0x2134: 0x094e, 0x2135: 0x04c5, - 0x2136: 0x0950, 0x2137: 0x14a0, 0x2138: 0x001e, 0x2139: 0x0960, 0x213a: 0x17fa, 0x213b: 0x049b, - 0x213c: 0x02c8, 0x213d: 0x0962, 0x213e: 0x0964, 0x213f: 0x096d, - // Block 0x85, offset 0x2140 - 0x2140: 0x04a6, 0x2141: 0x04c7, 0x2142: 0x04a8, 0x2143: 0x09df, 0x2144: 0x0906, 0x2145: 0x090b, - 0x2147: 0x090d, 0x2148: 0x090f, 0x2149: 0x14d9, 0x214a: 0x0914, - 0x214d: 0x091a, 0x214e: 0x091c, 0x214f: 0x091e, 0x2150: 0x0920, 0x2151: 0x0922, - 0x2152: 0x0924, 0x2153: 0x0929, 0x2154: 0x14c8, 0x2156: 0x17f6, 0x2157: 0x092d, - 0x2158: 0x092f, 0x2159: 0x155f, 0x215a: 0x0931, 0x215b: 0x1570, 0x215c: 0x17f8, - 0x215e: 0x0007, 0x215f: 0x093d, 0x2160: 0x0984, 0x2161: 0x093f, 0x2162: 0x0941, 0x2163: 0x098c, - 0x2164: 0x094c, 0x2165: 0x0494, 0x2166: 0x097c, 0x2167: 0x0499, 0x2168: 0x094e, 0x2169: 0x04c5, - 0x216a: 0x0950, 0x216b: 0x14a0, 0x216c: 0x001e, 0x216d: 0x0960, 0x216e: 0x17fa, 0x216f: 0x049b, - 0x2170: 0x02c8, 0x2171: 0x0962, 0x2172: 0x0964, 0x2173: 0x096d, 0x2174: 0x04a6, 0x2175: 0x04c7, - 0x2176: 0x04a8, 0x2177: 0x09df, 0x2178: 0x0906, 0x2179: 0x090b, 0x217b: 0x090d, - 0x217c: 0x090f, 0x217d: 0x14d9, 0x217e: 0x0914, - // Block 0x86, offset 0x2180 - 0x2180: 0x0918, 0x2181: 0x091a, 0x2182: 0x091c, 0x2183: 0x091e, 0x2184: 0x0920, - 0x2186: 0x0924, 0x218a: 0x17f6, 0x218b: 0x092d, - 0x218c: 0x092f, 0x218d: 0x155f, 0x218e: 0x0931, 0x218f: 0x1570, 0x2190: 0x17f8, - 0x2192: 0x0007, 0x2193: 0x093d, 0x2194: 0x0984, 0x2195: 0x093f, 0x2196: 0x0941, 0x2197: 0x098c, - 0x2198: 0x094c, 0x2199: 0x0494, 0x219a: 0x097c, 0x219b: 0x0499, 0x219c: 0x094e, 0x219d: 0x04c5, - 0x219e: 0x0950, 0x219f: 0x14a0, 0x21a0: 0x001e, 0x21a1: 0x0960, 0x21a2: 0x17fa, 0x21a3: 0x049b, - 0x21a4: 0x02c8, 0x21a5: 0x0962, 0x21a6: 0x0964, 0x21a7: 0x096d, 0x21a8: 0x04a6, 0x21a9: 0x04c7, - 0x21aa: 0x04a8, 0x21ab: 0x09df, 0x21ac: 0x0906, 0x21ad: 0x090b, 0x21ae: 0x14ad, 0x21af: 0x090d, - 0x21b0: 0x090f, 0x21b1: 0x14d9, 0x21b2: 0x0914, 0x21b3: 0x0916, 0x21b4: 0x0918, 0x21b5: 0x091a, - 0x21b6: 0x091c, 0x21b7: 0x091e, 0x21b8: 0x0920, 0x21b9: 0x0922, 0x21ba: 0x0924, 0x21bb: 0x0929, - 0x21bc: 0x14c8, 0x21bd: 0x092b, 0x21be: 0x17f6, 0x21bf: 0x092d, - // Block 0x87, offset 0x21c0 - 0x21c0: 0x092f, 0x21c1: 0x155f, 0x21c2: 0x0931, 0x21c3: 0x1570, 0x21c4: 0x17f8, 0x21c5: 0x14d4, - 0x21c6: 0x0007, 0x21c7: 0x093d, 0x21c8: 0x0984, 0x21c9: 0x093f, 0x21ca: 0x0941, 0x21cb: 0x098c, - 0x21cc: 0x094c, 0x21cd: 0x0494, 0x21ce: 0x097c, 0x21cf: 0x0499, 0x21d0: 0x094e, 0x21d1: 0x04c5, - 0x21d2: 0x0950, 0x21d3: 0x14a0, 0x21d4: 0x001e, 0x21d5: 0x0960, 0x21d6: 0x17fa, 0x21d7: 0x049b, - 0x21d8: 0x02c8, 0x21d9: 0x0962, 0x21da: 0x0964, 0x21db: 0x096d, 0x21dc: 0x04a6, 0x21dd: 0x04c7, - 0x21de: 0x04a8, 0x21df: 0x09df, 0x21e0: 0x0906, 0x21e1: 0x090b, 0x21e2: 0x14ad, 0x21e3: 0x090d, - 0x21e4: 0x090f, 0x21e5: 0x14d9, 0x21e6: 0x0914, 0x21e7: 0x0916, 0x21e8: 0x0918, 0x21e9: 0x091a, - 0x21ea: 0x091c, 0x21eb: 0x091e, 0x21ec: 0x0920, 0x21ed: 0x0922, 0x21ee: 0x0924, 0x21ef: 0x0929, - 0x21f0: 0x14c8, 0x21f1: 0x092b, 0x21f2: 0x17f6, 0x21f3: 0x092d, 0x21f4: 0x092f, 0x21f5: 0x155f, - 0x21f6: 0x0931, 0x21f7: 0x1570, 0x21f8: 0x17f8, 0x21f9: 0x14d4, 0x21fa: 0x0007, 0x21fb: 0x093d, - 0x21fc: 0x0984, 0x21fd: 0x093f, 0x21fe: 0x0941, 0x21ff: 0x098c, - // Block 0x88, offset 0x2200 - 0x2200: 0x094c, 0x2201: 0x0494, 0x2202: 0x097c, 0x2203: 0x0499, 0x2204: 0x094e, 0x2205: 0x04c5, - 0x2206: 0x0950, 0x2207: 0x14a0, 0x2208: 0x001e, 0x2209: 0x0960, 0x220a: 0x17fa, 0x220b: 0x049b, - 0x220c: 0x02c8, 0x220d: 0x0962, 0x220e: 0x0964, 0x220f: 0x096d, 0x2210: 0x04a6, 0x2211: 0x04c7, - 0x2212: 0x04a8, 0x2213: 0x09df, 0x2214: 0x0906, 0x2215: 0x090b, 0x2216: 0x14ad, 0x2217: 0x090d, - 0x2218: 0x090f, 0x2219: 0x14d9, 0x221a: 0x0914, 0x221b: 0x0916, 0x221c: 0x0918, 0x221d: 0x091a, - 0x221e: 0x091c, 0x221f: 0x091e, 0x2220: 0x0920, 0x2221: 0x0922, 0x2222: 0x0924, 0x2223: 0x0929, - 0x2224: 0x14c8, 0x2225: 0x092b, 0x2226: 0x17f6, 0x2227: 0x092d, 0x2228: 0x092f, 0x2229: 0x155f, - 0x222a: 0x0931, 0x222b: 0x1570, 0x222c: 0x17f8, 0x222d: 0x14d4, 0x222e: 0x0007, 0x222f: 0x093d, - 0x2230: 0x0984, 0x2231: 0x093f, 0x2232: 0x0941, 0x2233: 0x098c, 0x2234: 0x094c, 0x2235: 0x0494, - 0x2236: 0x097c, 0x2237: 0x0499, 0x2238: 0x094e, 0x2239: 0x04c5, 0x223a: 0x0950, 0x223b: 0x14a0, - 0x223c: 0x001e, 0x223d: 0x0960, 0x223e: 0x17fa, 0x223f: 0x049b, - // Block 0x89, offset 0x2240 - 0x2240: 0x02c8, 0x2241: 0x0962, 0x2242: 0x0964, 0x2243: 0x096d, 0x2244: 0x04a6, 0x2245: 0x04c7, - 0x2246: 0x04a8, 0x2247: 0x09df, 0x2248: 0x0906, 0x2249: 0x090b, 0x224a: 0x14ad, 0x224b: 0x090d, - 0x224c: 0x090f, 0x224d: 0x14d9, 0x224e: 0x0914, 0x224f: 0x0916, 0x2250: 0x0918, 0x2251: 0x091a, - 0x2252: 0x091c, 0x2253: 0x091e, 0x2254: 0x0920, 0x2255: 0x0922, 0x2256: 0x0924, 0x2257: 0x0929, - 0x2258: 0x14c8, 0x2259: 0x092b, 0x225a: 0x17f6, 0x225b: 0x092d, 0x225c: 0x092f, 0x225d: 0x155f, - 0x225e: 0x0931, 0x225f: 0x1570, 0x2260: 0x17f8, 0x2261: 0x14d4, 0x2262: 0x0007, 0x2263: 0x093d, - 0x2264: 0x0984, 0x2265: 0x093f, 0x2266: 0x0941, 0x2267: 0x098c, 0x2268: 0x094c, 0x2269: 0x0494, - 0x226a: 0x097c, 0x226b: 0x0499, 0x226c: 0x094e, 0x226d: 0x04c5, 0x226e: 0x0950, 0x226f: 0x14a0, - 0x2270: 0x001e, 0x2271: 0x0960, 0x2272: 0x17fa, 0x2273: 0x049b, 0x2274: 0x02c8, 0x2275: 0x0962, - 0x2276: 0x0964, 0x2277: 0x096d, 0x2278: 0x04a6, 0x2279: 0x04c7, 0x227a: 0x04a8, 0x227b: 0x09df, - 0x227c: 0x0906, 0x227d: 0x090b, 0x227e: 0x14ad, 0x227f: 0x090d, - // Block 0x8a, offset 0x2280 - 0x2280: 0x090f, 0x2281: 0x14d9, 0x2282: 0x0914, 0x2283: 0x0916, 0x2284: 0x0918, 0x2285: 0x091a, - 0x2286: 0x091c, 0x2287: 0x091e, 0x2288: 0x0920, 0x2289: 0x0922, 0x228a: 0x0924, 0x228b: 0x0929, - 0x228c: 0x14c8, 0x228d: 0x092b, 0x228e: 0x17f6, 0x228f: 0x092d, 0x2290: 0x092f, 0x2291: 0x155f, - 0x2292: 0x0931, 0x2293: 0x1570, 0x2294: 0x17f8, 0x2295: 0x14d4, 0x2296: 0x0007, 0x2297: 0x093d, - 0x2298: 0x0984, 0x2299: 0x093f, 0x229a: 0x0941, 0x229b: 0x098c, 0x229c: 0x094c, 0x229d: 0x0494, - 0x229e: 0x097c, 0x229f: 0x0499, 0x22a0: 0x094e, 0x22a1: 0x04c5, 0x22a2: 0x0950, 0x22a3: 0x14a0, - 0x22a4: 0x001e, 0x22a5: 0x0960, 0x22a6: 0x17fa, 0x22a7: 0x049b, 0x22a8: 0x02c8, 0x22a9: 0x0962, - 0x22aa: 0x0964, 0x22ab: 0x096d, 0x22ac: 0x04a6, 0x22ad: 0x04c7, 0x22ae: 0x04a8, 0x22af: 0x09df, - 0x22b0: 0x0906, 0x22b1: 0x090b, 0x22b2: 0x14ad, 0x22b3: 0x090d, 0x22b4: 0x090f, 0x22b5: 0x14d9, - 0x22b6: 0x0914, 0x22b7: 0x0916, 0x22b8: 0x0918, 0x22b9: 0x091a, 0x22ba: 0x091c, 0x22bb: 0x091e, - 0x22bc: 0x0920, 0x22bd: 0x0922, 0x22be: 0x0924, 0x22bf: 0x0929, - // Block 0x8b, offset 0x22c0 - 0x22c0: 0x14c8, 0x22c1: 0x092b, 0x22c2: 0x17f6, 0x22c3: 0x092d, 0x22c4: 0x092f, 0x22c5: 0x155f, - 0x22c6: 0x0931, 0x22c7: 0x1570, 0x22c8: 0x17f8, 0x22c9: 0x14d4, 0x22ca: 0x0007, 0x22cb: 0x093d, - 0x22cc: 0x0984, 0x22cd: 0x093f, 0x22ce: 0x0941, 0x22cf: 0x098c, 0x22d0: 0x094c, 0x22d1: 0x0494, - 0x22d2: 0x097c, 0x22d3: 0x0499, 0x22d4: 0x094e, 0x22d5: 0x04c5, 0x22d6: 0x0950, 0x22d7: 0x14a0, - 0x22d8: 0x001e, 0x22d9: 0x0960, 0x22da: 0x17fa, 0x22db: 0x049b, 0x22dc: 0x02c8, 0x22dd: 0x0962, - 0x22de: 0x0964, 0x22df: 0x096d, 0x22e0: 0x04a6, 0x22e1: 0x04c7, 0x22e2: 0x04a8, 0x22e3: 0x09df, - 0x22e4: 0x3b27, 0x22e5: 0x3b2a, 0x22e8: 0x3b2d, 0x22e9: 0x3b30, - 0x22ea: 0x14eb, 0x22eb: 0x3b33, 0x22ec: 0x3b36, 0x22ed: 0x3b39, 0x22ee: 0x3b3c, 0x22ef: 0x057b, - 0x22f0: 0x3b3f, 0x22f1: 0x3b42, 0x22f2: 0x3b45, 0x22f3: 0x3b48, 0x22f4: 0x3b4b, 0x22f5: 0x3b4e, - 0x22f6: 0x3b51, 0x22f7: 0x14ee, 0x22f8: 0x3b54, 0x22f9: 0x057b, 0x22fa: 0x0581, 0x22fb: 0x3b57, - 0x22fc: 0x055f, 0x22fd: 0x3b5a, 0x22fe: 0x3b5d, 0x22ff: 0x3b60, - // Block 0x8c, offset 0x2300 - 0x2300: 0x14d6, 0x2301: 0x3b63, 0x2302: 0x3b67, 0x2303: 0x0559, 0x2304: 0x0973, 0x2305: 0x0976, - 0x2306: 0x057e, 0x2307: 0x3b6a, 0x2308: 0x3b6d, 0x2309: 0x055c, 0x230a: 0x12fd, 0x230b: 0x0572, - 0x230c: 0x3b70, 0x230d: 0x0015, 0x230e: 0x3b73, 0x230f: 0x3b76, 0x2310: 0x3b79, 0x2311: 0x056f, - 0x2312: 0x0575, 0x2313: 0x0578, 0x2314: 0x3b7c, 0x2315: 0x3b7f, 0x2316: 0x3b82, 0x2317: 0x056c, - 0x2318: 0x0979, 0x2319: 0x3b85, 0x231a: 0x3b88, 0x231b: 0x3b8b, 0x231c: 0x057e, 0x231d: 0x055c, - 0x231e: 0x0572, 0x231f: 0x056c, 0x2320: 0x0575, 0x2321: 0x056f, 0x2322: 0x3b2d, 0x2323: 0x3b30, - 0x2324: 0x14eb, 0x2325: 0x3b33, 0x2326: 0x3b36, 0x2327: 0x3b39, 0x2328: 0x3b3c, 0x2329: 0x057b, - 0x232a: 0x3b3f, 0x232b: 0x3b42, 0x232c: 0x3b45, 0x232d: 0x3b48, 0x232e: 0x3b4b, 0x232f: 0x3b4e, - 0x2330: 0x3b51, 0x2331: 0x14ee, 0x2332: 0x3b54, 0x2333: 0x057b, 0x2334: 0x0581, 0x2335: 0x3b57, - 0x2336: 0x055f, 0x2337: 0x3b5a, 0x2338: 0x3b5d, 0x2339: 0x3b60, 0x233a: 0x14d6, 0x233b: 0x3b63, - 0x233c: 0x3b67, 0x233d: 0x0559, 0x233e: 0x0973, 0x233f: 0x0976, - // Block 0x8d, offset 0x2340 - 0x2340: 0x057e, 0x2341: 0x3b6a, 0x2342: 0x3b6d, 0x2343: 0x055c, 0x2344: 0x12fd, 0x2345: 0x0572, - 0x2346: 0x3b70, 0x2347: 0x0015, 0x2348: 0x3b73, 0x2349: 0x3b76, 0x234a: 0x3b79, 0x234b: 0x056f, - 0x234c: 0x0575, 0x234d: 0x0578, 0x234e: 0x3b7c, 0x234f: 0x3b7f, 0x2350: 0x3b82, 0x2351: 0x056c, - 0x2352: 0x0979, 0x2353: 0x3b85, 0x2354: 0x3b88, 0x2355: 0x3b8b, 0x2356: 0x057e, 0x2357: 0x055c, - 0x2358: 0x0572, 0x2359: 0x056c, 0x235a: 0x0575, 0x235b: 0x056f, 0x235c: 0x3b2d, 0x235d: 0x3b30, - 0x235e: 0x14eb, 0x235f: 0x3b33, 0x2360: 0x3b36, 0x2361: 0x3b39, 0x2362: 0x3b3c, 0x2363: 0x057b, - 0x2364: 0x3b3f, 0x2365: 0x3b42, 0x2366: 0x3b45, 0x2367: 0x3b48, 0x2368: 0x3b4b, 0x2369: 0x3b4e, - 0x236a: 0x3b51, 0x236b: 0x14ee, 0x236c: 0x3b54, 0x236d: 0x057b, 0x236e: 0x0581, 0x236f: 0x3b57, - 0x2370: 0x055f, 0x2371: 0x3b5a, 0x2372: 0x3b5d, 0x2373: 0x3b60, 0x2374: 0x14d6, 0x2375: 0x3b63, - 0x2376: 0x3b67, 0x2377: 0x0559, 0x2378: 0x0973, 0x2379: 0x0976, 0x237a: 0x057e, 0x237b: 0x3b6a, - 0x237c: 0x3b6d, 0x237d: 0x055c, 0x237e: 0x12fd, 0x237f: 0x0572, - // Block 0x8e, offset 0x2380 - 0x2380: 0x3b70, 0x2381: 0x0015, 0x2382: 0x3b73, 0x2383: 0x3b76, 0x2384: 0x3b79, 0x2385: 0x056f, - 0x2386: 0x0575, 0x2387: 0x0578, 0x2388: 0x3b7c, 0x2389: 0x3b7f, 0x238a: 0x3b82, 0x238b: 0x056c, - 0x238c: 0x0979, 0x238d: 0x3b85, 0x238e: 0x3b88, 0x238f: 0x3b8b, 0x2390: 0x057e, 0x2391: 0x055c, - 0x2392: 0x0572, 0x2393: 0x056c, 0x2394: 0x0575, 0x2395: 0x056f, 0x2396: 0x3b2d, 0x2397: 0x3b30, - 0x2398: 0x14eb, 0x2399: 0x3b33, 0x239a: 0x3b36, 0x239b: 0x3b39, 0x239c: 0x3b3c, 0x239d: 0x057b, - 0x239e: 0x3b3f, 0x239f: 0x3b42, 0x23a0: 0x3b45, 0x23a1: 0x3b48, 0x23a2: 0x3b4b, 0x23a3: 0x3b4e, - 0x23a4: 0x3b51, 0x23a5: 0x14ee, 0x23a6: 0x3b54, 0x23a7: 0x057b, 0x23a8: 0x0581, 0x23a9: 0x3b57, - 0x23aa: 0x055f, 0x23ab: 0x3b5a, 0x23ac: 0x3b5d, 0x23ad: 0x3b60, 0x23ae: 0x14d6, 0x23af: 0x3b63, - 0x23b0: 0x3b67, 0x23b1: 0x0559, 0x23b2: 0x0973, 0x23b3: 0x0976, 0x23b4: 0x057e, 0x23b5: 0x3b6a, - 0x23b6: 0x3b6d, 0x23b7: 0x055c, 0x23b8: 0x12fd, 0x23b9: 0x0572, 0x23ba: 0x3b70, 0x23bb: 0x0015, - 0x23bc: 0x3b73, 0x23bd: 0x3b76, 0x23be: 0x3b79, 0x23bf: 0x056f, - // Block 0x8f, offset 0x23c0 - 0x23c0: 0x0575, 0x23c1: 0x0578, 0x23c2: 0x3b7c, 0x23c3: 0x3b7f, 0x23c4: 0x3b82, 0x23c5: 0x056c, - 0x23c6: 0x0979, 0x23c7: 0x3b85, 0x23c8: 0x3b88, 0x23c9: 0x3b8b, 0x23ca: 0x057e, 0x23cb: 0x055c, - 0x23cc: 0x0572, 0x23cd: 0x056c, 0x23ce: 0x0575, 0x23cf: 0x056f, 0x23d0: 0x3b2d, 0x23d1: 0x3b30, - 0x23d2: 0x14eb, 0x23d3: 0x3b33, 0x23d4: 0x3b36, 0x23d5: 0x3b39, 0x23d6: 0x3b3c, 0x23d7: 0x057b, - 0x23d8: 0x3b3f, 0x23d9: 0x3b42, 0x23da: 0x3b45, 0x23db: 0x3b48, 0x23dc: 0x3b4b, 0x23dd: 0x3b4e, - 0x23de: 0x3b51, 0x23df: 0x14ee, 0x23e0: 0x3b54, 0x23e1: 0x057b, 0x23e2: 0x0581, 0x23e3: 0x3b57, - 0x23e4: 0x055f, 0x23e5: 0x3b5a, 0x23e6: 0x3b5d, 0x23e7: 0x3b60, 0x23e8: 0x14d6, 0x23e9: 0x3b63, - 0x23ea: 0x3b67, 0x23eb: 0x0559, 0x23ec: 0x0973, 0x23ed: 0x0976, 0x23ee: 0x057e, 0x23ef: 0x3b6a, - 0x23f0: 0x3b6d, 0x23f1: 0x055c, 0x23f2: 0x12fd, 0x23f3: 0x0572, 0x23f4: 0x3b70, 0x23f5: 0x0015, - 0x23f6: 0x3b73, 0x23f7: 0x3b76, 0x23f8: 0x3b79, 0x23f9: 0x056f, 0x23fa: 0x0575, 0x23fb: 0x0578, - 0x23fc: 0x3b7c, 0x23fd: 0x3b7f, 0x23fe: 0x3b82, 0x23ff: 0x056c, - // Block 0x90, offset 0x2400 - 0x2400: 0x0979, 0x2401: 0x3b85, 0x2402: 0x3b88, 0x2403: 0x3b8b, 0x2404: 0x057e, 0x2405: 0x055c, - 0x2406: 0x0572, 0x2407: 0x056c, 0x2408: 0x0575, 0x2409: 0x056f, 0x240a: 0x3b8f, 0x240b: 0x3b92, - 0x240e: 0x1486, 0x240f: 0x001c, 0x2410: 0x000d, 0x2411: 0x000f, - 0x2412: 0x1488, 0x2413: 0x148a, 0x2414: 0x148c, 0x2415: 0x148e, 0x2416: 0x1490, 0x2417: 0x1492, - 0x2418: 0x1486, 0x2419: 0x001c, 0x241a: 0x000d, 0x241b: 0x000f, 0x241c: 0x1488, 0x241d: 0x148a, - 0x241e: 0x148c, 0x241f: 0x148e, 0x2420: 0x1490, 0x2421: 0x1492, 0x2422: 0x1486, 0x2423: 0x001c, - 0x2424: 0x000d, 0x2425: 0x000f, 0x2426: 0x1488, 0x2427: 0x148a, 0x2428: 0x148c, 0x2429: 0x148e, - 0x242a: 0x1490, 0x242b: 0x1492, 0x242c: 0x1486, 0x242d: 0x001c, 0x242e: 0x000d, 0x242f: 0x000f, - 0x2430: 0x1488, 0x2431: 0x148a, 0x2432: 0x148c, 0x2433: 0x148e, 0x2434: 0x1490, 0x2435: 0x1492, - 0x2436: 0x1486, 0x2437: 0x001c, 0x2438: 0x000d, 0x2439: 0x000f, 0x243a: 0x1488, 0x243b: 0x148a, - 0x243c: 0x148c, 0x243d: 0x148e, 0x243e: 0x1490, 0x243f: 0x1492, - // Block 0x91, offset 0x2440 - 0x2440: 0x3b95, 0x2441: 0x3b98, 0x2442: 0x3b9b, 0x2443: 0x3b9e, 0x2444: 0x3ba1, 0x2445: 0x3ba4, - 0x2446: 0x3ba7, 0x2447: 0x3baa, 0x2448: 0x3bad, 0x2449: 0x3bb0, 0x244a: 0x3bb3, - 0x2450: 0x3bb6, 0x2451: 0x3bba, - 0x2452: 0x3bbe, 0x2453: 0x3bc2, 0x2454: 0x3bc6, 0x2455: 0x3bca, 0x2456: 0x3bce, 0x2457: 0x3bd2, - 0x2458: 0x3bd6, 0x2459: 0x3bda, 0x245a: 0x3bde, 0x245b: 0x3be2, 0x245c: 0x3be6, 0x245d: 0x3bea, - 0x245e: 0x3bee, 0x245f: 0x3bf2, 0x2460: 0x3bf6, 0x2461: 0x3bfa, 0x2462: 0x3bfe, 0x2463: 0x3c02, - 0x2464: 0x3c06, 0x2465: 0x3c0a, 0x2466: 0x3c0e, 0x2467: 0x3c12, 0x2468: 0x3c16, 0x2469: 0x3c1a, - 0x246a: 0x3c1e, 0x246b: 0x14ad, 0x246c: 0x092b, 0x246d: 0x3c26, 0x246e: 0x3c29, - 0x2470: 0x0906, 0x2471: 0x090b, 0x2472: 0x14ad, 0x2473: 0x090d, 0x2474: 0x090f, 0x2475: 0x14d9, - 0x2476: 0x0914, 0x2477: 0x0916, 0x2478: 0x0918, 0x2479: 0x091a, 0x247a: 0x091c, 0x247b: 0x091e, - 0x247c: 0x0920, 0x247d: 0x0922, 0x247e: 0x0924, 0x247f: 0x0929, - // Block 0x92, offset 0x2480 - 0x2480: 0x14c8, 0x2481: 0x092b, 0x2482: 0x17f6, 0x2483: 0x092d, 0x2484: 0x092f, 0x2485: 0x155f, - 0x2486: 0x0931, 0x2487: 0x1570, 0x2488: 0x17f8, 0x2489: 0x14d4, 0x248a: 0x3c2c, 0x248b: 0x293d, - 0x248c: 0x3c2f, 0x248d: 0x3c32, 0x248e: 0x3c35, 0x248f: 0x3c39, - // Block 0x93, offset 0x24c0 - 0x24d0: 0x3c3c, - // Block 0x94, offset 0x2500 - 0x2500: 0x3c3f, 0x2501: 0x3c46, 0x2502: 0x2291, - 0x2510: 0x1922, 0x2511: 0x3c4d, - 0x2512: 0x3c51, 0x2513: 0x1cb3, 0x2514: 0x183e, 0x2515: 0x3c55, 0x2516: 0x3c59, 0x2517: 0x1ed0, - 0x2518: 0x3c5d, 0x2519: 0x3c61, 0x251a: 0x3c65, 0x251b: 0x2d49, 0x251c: 0x3c69, 0x251d: 0x3c6d, - 0x251e: 0x3c71, 0x251f: 0x3c75, 0x2520: 0x3c79, 0x2521: 0x3c7d, 0x2522: 0x19b2, 0x2523: 0x3c81, - 0x2524: 0x3c85, 0x2525: 0x3c89, 0x2526: 0x3c8d, 0x2527: 0x3c91, 0x2528: 0x3c95, 0x2529: 0x1826, - 0x252a: 0x1eb0, 0x252b: 0x3c99, 0x252c: 0x21c7, 0x252d: 0x1ebc, 0x252e: 0x21cb, 0x252f: 0x3c9d, - 0x2530: 0x1a92, 0x2531: 0x3ca1, 0x2532: 0x3ca5, 0x2533: 0x3ca9, 0x2534: 0x3cad, 0x2535: 0x3cb1, - 0x2536: 0x2183, 0x2537: 0x194a, 0x2538: 0x3cb5, 0x2539: 0x3cb9, 0x253a: 0x3cbd, - // Block 0x95, offset 0x2540 - 0x2540: 0x3cc1, 0x2541: 0x3ccb, 0x2542: 0x3cd5, 0x2543: 0x3cdf, 0x2544: 0x3ce9, 0x2545: 0x3cf3, - 0x2546: 0x3cfd, 0x2547: 0x3d07, 0x2548: 0x3d11, - 0x2550: 0x3d1b, 0x2551: 0x3d1f, - // Block 0x96, offset 0x2580 - 0x2580: 0x3d23, 0x2581: 0x3d27, 0x2582: 0x3d2b, 0x2583: 0x3d2f, 0x2584: 0x3d34, 0x2585: 0x2eb5, - 0x2586: 0x3d38, 0x2587: 0x3d3c, 0x2588: 0x3d40, 0x2589: 0x3d44, 0x258a: 0x2eb9, 0x258b: 0x3d48, - 0x258c: 0x3d4c, 0x258d: 0x3d50, 0x258e: 0x2ebd, 0x258f: 0x3d55, 0x2590: 0x3d59, 0x2591: 0x3d5d, - 0x2592: 0x3d61, 0x2593: 0x3d66, 0x2594: 0x3d6a, 0x2595: 0x3c71, 0x2596: 0x3d6e, 0x2597: 0x3d73, - 0x2598: 0x3d77, 0x2599: 0x3d7b, 0x259a: 0x3d7f, 0x259b: 0x2f9a, 0x259c: 0x3d83, 0x259d: 0x1866, - 0x259e: 0x3d88, 0x259f: 0x3d8c, 0x25a0: 0x3d90, 0x25a1: 0x3d94, 0x25a2: 0x3cb9, 0x25a3: 0x3d98, - 0x25a4: 0x3d9c, 0x25a5: 0x2fae, 0x25a6: 0x2ec1, 0x25a7: 0x2ec5, 0x25a8: 0x2fb2, 0x25a9: 0x3da0, - 0x25aa: 0x3da4, 0x25ab: 0x2bf1, 0x25ac: 0x3da8, 0x25ad: 0x2ec9, 0x25ae: 0x3dac, 0x25af: 0x3db0, - 0x25b0: 0x3db4, 0x25b1: 0x3db8, 0x25b2: 0x3db8, 0x25b3: 0x3db8, 0x25b4: 0x3dbc, 0x25b5: 0x3dc1, - 0x25b6: 0x3dc5, 0x25b7: 0x3dc9, 0x25b8: 0x3dcd, 0x25b9: 0x3dd2, 0x25ba: 0x3dd6, 0x25bb: 0x3dda, - 0x25bc: 0x3dde, 0x25bd: 0x3de2, 0x25be: 0x3de6, 0x25bf: 0x3dea, - // Block 0x97, offset 0x25c0 - 0x25c0: 0x3dee, 0x25c1: 0x3df2, 0x25c2: 0x3df6, 0x25c3: 0x3dfa, 0x25c4: 0x3dfe, 0x25c5: 0x3e02, - 0x25c6: 0x3e02, 0x25c7: 0x2fba, 0x25c8: 0x3e06, 0x25c9: 0x3e0a, 0x25ca: 0x3e0e, 0x25cb: 0x3e12, - 0x25cc: 0x2ed1, 0x25cd: 0x3e16, 0x25ce: 0x3e1a, 0x25cf: 0x3e1e, 0x25d0: 0x2e39, 0x25d1: 0x3e22, - 0x25d2: 0x3e26, 0x25d3: 0x3e2a, 0x25d4: 0x3e2e, 0x25d5: 0x3e32, 0x25d6: 0x3e36, 0x25d7: 0x3e3a, - 0x25d8: 0x3e3e, 0x25d9: 0x3e42, 0x25da: 0x3e47, 0x25db: 0x3e4b, 0x25dc: 0x3e4f, 0x25dd: 0x3c55, - 0x25de: 0x3e53, 0x25df: 0x3e57, 0x25e0: 0x3e5b, 0x25e1: 0x3e60, 0x25e2: 0x3e65, 0x25e3: 0x3e69, - 0x25e4: 0x3e6d, 0x25e5: 0x3e71, 0x25e6: 0x3e75, 0x25e7: 0x3e79, 0x25e8: 0x3e7d, 0x25e9: 0x3e81, - 0x25ea: 0x3e85, 0x25eb: 0x3e85, 0x25ec: 0x3e89, 0x25ed: 0x3e8e, 0x25ee: 0x3e92, 0x25ef: 0x2be1, - 0x25f0: 0x3e96, 0x25f1: 0x3e9a, 0x25f2: 0x3e9f, 0x25f3: 0x3ea3, 0x25f4: 0x3ea7, 0x25f5: 0x18ce, - 0x25f6: 0x3eab, 0x25f7: 0x3eaf, 0x25f8: 0x18d6, 0x25f9: 0x3eb3, 0x25fa: 0x3eb7, 0x25fb: 0x3ebb, - 0x25fc: 0x3ec0, 0x25fd: 0x3ec4, 0x25fe: 0x3ec9, 0x25ff: 0x3ecd, - // Block 0x98, offset 0x2600 - 0x2600: 0x3ed1, 0x2601: 0x3ed5, 0x2602: 0x3ed9, 0x2603: 0x3edd, 0x2604: 0x3ee1, 0x2605: 0x3ee5, - 0x2606: 0x3ee9, 0x2607: 0x3eed, 0x2608: 0x3ef1, 0x2609: 0x3ef5, 0x260a: 0x3efa, 0x260b: 0x3efe, - 0x260c: 0x3f02, 0x260d: 0x3f06, 0x260e: 0x2b11, 0x260f: 0x3f0a, 0x2610: 0x18fe, 0x2611: 0x3f0f, - 0x2612: 0x3f0f, 0x2613: 0x3f14, 0x2614: 0x3f18, 0x2615: 0x3f18, 0x2616: 0x3f1c, 0x2617: 0x3f20, - 0x2618: 0x3f25, 0x2619: 0x3f2a, 0x261a: 0x3f2e, 0x261b: 0x3f32, 0x261c: 0x3f36, 0x261d: 0x3f3a, - 0x261e: 0x3f3e, 0x261f: 0x3f42, 0x2620: 0x3f46, 0x2621: 0x3f4a, 0x2622: 0x3f4e, 0x2623: 0x2ee5, - 0x2624: 0x3f52, 0x2625: 0x3f57, 0x2626: 0x3f5b, 0x2627: 0x3f5f, 0x2628: 0x2fea, 0x2629: 0x3f5f, - 0x262a: 0x3f63, 0x262b: 0x2eed, 0x262c: 0x3f67, 0x262d: 0x3f6b, 0x262e: 0x3f6f, 0x262f: 0x3f73, - 0x2630: 0x2ef1, 0x2631: 0x2aa5, 0x2632: 0x3f77, 0x2633: 0x3f7b, 0x2634: 0x3f7f, 0x2635: 0x3f83, - 0x2636: 0x3f87, 0x2637: 0x3f8b, 0x2638: 0x3f8f, 0x2639: 0x3f94, 0x263a: 0x3f98, 0x263b: 0x3f9c, - 0x263c: 0x3fa0, 0x263d: 0x3fa4, 0x263e: 0x3fa8, 0x263f: 0x3fad, - // Block 0x99, offset 0x2640 - 0x2640: 0x3fb1, 0x2641: 0x3fb5, 0x2642: 0x3fb9, 0x2643: 0x3fbd, 0x2644: 0x3fc1, 0x2645: 0x3fc5, - 0x2646: 0x3fc9, 0x2647: 0x3fcd, 0x2648: 0x2ef5, 0x2649: 0x3fd1, 0x264a: 0x3fd5, 0x264b: 0x3fda, - 0x264c: 0x3fde, 0x264d: 0x3fe2, 0x264e: 0x3fe6, 0x264f: 0x2efd, 0x2650: 0x3fea, 0x2651: 0x3fee, - 0x2652: 0x3ff2, 0x2653: 0x3ff6, 0x2654: 0x3ffa, 0x2655: 0x3ffe, 0x2656: 0x4002, 0x2657: 0x4006, - 0x2658: 0x2b15, 0x2659: 0x300a, 0x265a: 0x400a, 0x265b: 0x400e, 0x265c: 0x4012, 0x265d: 0x4016, - 0x265e: 0x401b, 0x265f: 0x401f, 0x2660: 0x4023, 0x2661: 0x4027, 0x2662: 0x2f01, 0x2663: 0x402b, - 0x2664: 0x4030, 0x2665: 0x4034, 0x2666: 0x4038, 0x2667: 0x30b5, 0x2668: 0x403c, 0x2669: 0x4040, - 0x266a: 0x4044, 0x266b: 0x4048, 0x266c: 0x404c, 0x266d: 0x4051, 0x266e: 0x4055, 0x266f: 0x4059, - 0x2670: 0x405d, 0x2671: 0x4062, 0x2672: 0x4066, 0x2673: 0x406a, 0x2674: 0x406e, 0x2675: 0x2c25, - 0x2676: 0x4072, 0x2677: 0x4076, 0x2678: 0x407b, 0x2679: 0x4080, 0x267a: 0x4085, 0x267b: 0x4089, - 0x267c: 0x408e, 0x267d: 0x4092, 0x267e: 0x4096, 0x267f: 0x409a, - // Block 0x9a, offset 0x2680 - 0x2680: 0x409e, 0x2681: 0x2f05, 0x2682: 0x2d71, 0x2683: 0x40a2, 0x2684: 0x40a6, 0x2685: 0x40aa, - 0x2686: 0x40ae, 0x2687: 0x40b3, 0x2688: 0x40b7, 0x2689: 0x40bb, 0x268a: 0x40bf, 0x268b: 0x3016, - 0x268c: 0x40c3, 0x268d: 0x40c7, 0x268e: 0x40cc, 0x268f: 0x40d0, 0x2690: 0x40d4, 0x2691: 0x40d9, - 0x2692: 0x40de, 0x2693: 0x40e2, 0x2694: 0x301a, 0x2695: 0x40e6, 0x2696: 0x40ea, 0x2697: 0x40ee, - 0x2698: 0x40f2, 0x2699: 0x40f6, 0x269a: 0x40fa, 0x269b: 0x40fe, 0x269c: 0x4103, 0x269d: 0x4107, - 0x269e: 0x410c, 0x269f: 0x4110, 0x26a0: 0x4115, 0x26a1: 0x3022, 0x26a2: 0x4119, 0x26a3: 0x411d, - 0x26a4: 0x4122, 0x26a5: 0x4126, 0x26a6: 0x412a, 0x26a7: 0x412f, 0x26a8: 0x4134, 0x26a9: 0x4138, - 0x26aa: 0x413c, 0x26ab: 0x4140, 0x26ac: 0x4144, 0x26ad: 0x4144, 0x26ae: 0x4148, 0x26af: 0x414c, - 0x26b0: 0x302a, 0x26b1: 0x4150, 0x26b2: 0x4154, 0x26b3: 0x4158, 0x26b4: 0x415c, 0x26b5: 0x4160, - 0x26b6: 0x4165, 0x26b7: 0x4169, 0x26b8: 0x2bed, 0x26b9: 0x416e, 0x26ba: 0x4173, 0x26bb: 0x4177, - 0x26bc: 0x417c, 0x26bd: 0x4181, 0x26be: 0x4186, 0x26bf: 0x418a, - // Block 0x9b, offset 0x26c0 - 0x26c0: 0x3042, 0x26c1: 0x418e, 0x26c2: 0x4193, 0x26c3: 0x4198, 0x26c4: 0x419d, 0x26c5: 0x41a2, - 0x26c6: 0x41a6, 0x26c7: 0x41a6, 0x26c8: 0x3046, 0x26c9: 0x30bd, 0x26ca: 0x41aa, 0x26cb: 0x41ae, - 0x26cc: 0x41b2, 0x26cd: 0x41b6, 0x26ce: 0x41bb, 0x26cf: 0x2b59, 0x26d0: 0x304e, 0x26d1: 0x41bf, - 0x26d2: 0x41c3, 0x26d3: 0x2f2d, 0x26d4: 0x41c8, 0x26d5: 0x41cd, 0x26d6: 0x2e89, 0x26d7: 0x41d2, - 0x26d8: 0x41d6, 0x26d9: 0x2f39, 0x26da: 0x41da, 0x26db: 0x41de, 0x26dc: 0x41e2, 0x26dd: 0x41e7, - 0x26de: 0x41e7, 0x26df: 0x41ec, 0x26e0: 0x41f0, 0x26e1: 0x41f4, 0x26e2: 0x41f9, 0x26e3: 0x41fd, - 0x26e4: 0x4201, 0x26e5: 0x4205, 0x26e6: 0x420a, 0x26e7: 0x420e, 0x26e8: 0x4212, 0x26e9: 0x4216, - 0x26ea: 0x421a, 0x26eb: 0x421e, 0x26ec: 0x4223, 0x26ed: 0x4227, 0x26ee: 0x422b, 0x26ef: 0x422f, - 0x26f0: 0x4233, 0x26f1: 0x4237, 0x26f2: 0x423b, 0x26f3: 0x4240, 0x26f4: 0x4245, 0x26f5: 0x4249, - 0x26f6: 0x424e, 0x26f7: 0x4252, 0x26f8: 0x4257, 0x26f9: 0x425b, 0x26fa: 0x2f51, 0x26fb: 0x425f, - 0x26fc: 0x4264, 0x26fd: 0x4269, 0x26fe: 0x426d, 0x26ff: 0x4272, - // Block 0x9c, offset 0x2700 - 0x2700: 0x4276, 0x2701: 0x427b, 0x2702: 0x427f, 0x2703: 0x4283, 0x2704: 0x4287, 0x2705: 0x428b, - 0x2706: 0x428f, 0x2707: 0x4293, 0x2708: 0x4298, 0x2709: 0x429d, 0x270a: 0x42a2, 0x270b: 0x3f14, - 0x270c: 0x42a7, 0x270d: 0x42ab, 0x270e: 0x42af, 0x270f: 0x42b3, 0x2710: 0x42b7, 0x2711: 0x42bb, - 0x2712: 0x42bf, 0x2713: 0x42c3, 0x2714: 0x42c7, 0x2715: 0x42cb, 0x2716: 0x42cf, 0x2717: 0x42d3, - 0x2718: 0x2c31, 0x2719: 0x42d8, 0x271a: 0x42dc, 0x271b: 0x42e0, 0x271c: 0x42e4, 0x271d: 0x42e8, - 0x271e: 0x42ec, 0x271f: 0x2f5d, 0x2720: 0x42f0, 0x2721: 0x42f4, 0x2722: 0x42f8, 0x2723: 0x42fc, - 0x2724: 0x4300, 0x2725: 0x4305, 0x2726: 0x430a, 0x2727: 0x430f, 0x2728: 0x4313, 0x2729: 0x4317, - 0x272a: 0x431b, 0x272b: 0x431f, 0x272c: 0x4324, 0x272d: 0x4328, 0x272e: 0x432d, 0x272f: 0x4331, - 0x2730: 0x4335, 0x2731: 0x433a, 0x2732: 0x433f, 0x2733: 0x4343, 0x2734: 0x2b45, 0x2735: 0x4347, - 0x2736: 0x434b, 0x2737: 0x434f, 0x2738: 0x4353, 0x2739: 0x4357, 0x273a: 0x435b, 0x273b: 0x306a, - 0x273c: 0x435f, 0x273d: 0x4363, 0x273e: 0x4367, 0x273f: 0x436b, - // Block 0x9d, offset 0x2740 - 0x2740: 0x436f, 0x2741: 0x4373, 0x2742: 0x4377, 0x2743: 0x437b, 0x2744: 0x1a66, 0x2745: 0x437f, - 0x2746: 0x4384, 0x2747: 0x4388, 0x2748: 0x438c, 0x2749: 0x4390, 0x274a: 0x4394, 0x274b: 0x4398, - 0x274c: 0x439d, 0x274d: 0x43a2, 0x274e: 0x43a6, 0x274f: 0x43aa, 0x2750: 0x307e, 0x2751: 0x3082, - 0x2752: 0x1a82, 0x2753: 0x43ae, 0x2754: 0x43b3, 0x2755: 0x43b7, 0x2756: 0x43bb, 0x2757: 0x43bf, - 0x2758: 0x43c3, 0x2759: 0x43c8, 0x275a: 0x43cd, 0x275b: 0x43d1, 0x275c: 0x43d5, 0x275d: 0x43d9, - 0x275e: 0x43de, 0x275f: 0x3086, 0x2760: 0x43e2, 0x2761: 0x43e7, 0x2762: 0x43ec, 0x2763: 0x43f0, - 0x2764: 0x43f4, 0x2765: 0x43f8, 0x2766: 0x43fd, 0x2767: 0x4401, 0x2768: 0x4405, 0x2769: 0x4409, - 0x276a: 0x440d, 0x276b: 0x4411, 0x276c: 0x4415, 0x276d: 0x4419, 0x276e: 0x441e, 0x276f: 0x4422, - 0x2770: 0x4426, 0x2771: 0x442a, 0x2772: 0x442f, 0x2773: 0x4433, 0x2774: 0x4437, 0x2775: 0x443b, - 0x2776: 0x443f, 0x2777: 0x4444, 0x2778: 0x4449, 0x2779: 0x444d, 0x277a: 0x4451, 0x277b: 0x4455, - 0x277c: 0x445a, 0x277d: 0x445e, 0x277e: 0x309e, 0x277f: 0x309e, - // Block 0x9e, offset 0x2780 - 0x2780: 0x4463, 0x2781: 0x4467, 0x2782: 0x446c, 0x2783: 0x4470, 0x2784: 0x4474, 0x2785: 0x4478, - 0x2786: 0x447c, 0x2787: 0x4480, 0x2788: 0x4484, 0x2789: 0x4488, 0x278a: 0x30a2, 0x278b: 0x448d, - 0x278c: 0x4491, 0x278d: 0x4495, 0x278e: 0x4499, 0x278f: 0x449d, 0x2790: 0x44a1, 0x2791: 0x44a6, - 0x2792: 0x44aa, 0x2793: 0x44af, 0x2794: 0x44b4, 0x2795: 0x1b42, 0x2796: 0x44b9, 0x2797: 0x1b52, - 0x2798: 0x44bd, 0x2799: 0x44c1, 0x279a: 0x44c5, 0x279b: 0x44c9, 0x279c: 0x1b66, 0x279d: 0x44cd, + 0x1040: 0x436f, 0x1041: 0x4373, 0x1042: 0x4377, 0x1043: 0x437b, 0x1044: 0x1a66, 0x1045: 0x437f, + 0x1046: 0x4384, 0x1047: 0x4388, 0x1048: 0x438c, 0x1049: 0x4390, 0x104a: 0x4394, 0x104b: 0x4398, + 0x104c: 0x439d, 0x104d: 0x43a2, 0x104e: 0x43a6, 0x104f: 0x43aa, 0x1050: 0x307e, 0x1051: 0x3082, + 0x1052: 0x1a82, 0x1053: 0x43ae, 0x1054: 0x43b3, 0x1055: 0x43b7, 0x1056: 0x43bb, 0x1057: 0x43bf, + 0x1058: 0x43c3, 0x1059: 0x43c8, 0x105a: 0x43cd, 0x105b: 0x43d1, 0x105c: 0x43d5, 0x105d: 0x43d9, + 0x105e: 0x43de, 0x105f: 0x3086, 0x1060: 0x43e2, 0x1061: 0x43e7, 0x1062: 0x43ec, 0x1063: 0x43f0, + 0x1064: 0x43f4, 0x1065: 0x43f8, 0x1066: 0x43fd, 0x1067: 0x4401, 0x1068: 0x4405, 0x1069: 0x4409, + 0x106a: 0x440d, 0x106b: 0x4411, 0x106c: 0x4415, 0x106d: 0x4419, 0x106e: 0x441e, 0x106f: 0x4422, + 0x1070: 0x4426, 0x1071: 0x442a, 0x1072: 0x442f, 0x1073: 0x4433, 0x1074: 0x4437, 0x1075: 0x443b, + 0x1076: 0x443f, 0x1077: 0x4444, 0x1078: 0x4449, 0x1079: 0x444d, 0x107a: 0x4451, 0x107b: 0x4455, + 0x107c: 0x445a, 0x107d: 0x445e, 0x107e: 0x309e, 0x107f: 0x309e, +} + +// nfkcDecompSparseOffset: 93 entries, 186 bytes +var nfkcDecompSparseOffset = []uint16{0x0, 0xc, 0x16, 0x1e, 0x24, 0x27, 0x31, 0x37, 0x3e, 0x44, 0x4c, 0x59, 0x60, 0x66, 0x6e, 0x70, 0x72, 0x74, 0x78, 0x7c, 0x7e, 0x82, 0x85, 0x88, 0x8c, 0x8e, 0x90, 0x92, 0x96, 0x98, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xae, 0xb6, 0xb8, 0xba, 0xc3, 0xc6, 0xcd, 0xd8, 0xe6, 0xf4, 0xfe, 0x102, 0x104, 0x10e, 0x11a, 0x11f, 0x122, 0x124, 0x126, 0x129, 0x12b, 0x12d, 0x12f, 0x131, 0x133, 0x135, 0x137, 0x139, 0x13b, 0x140, 0x14f, 0x15d, 0x15f, 0x161, 0x169, 0x179, 0x17b, 0x186, 0x18d, 0x198, 0x1a4, 0x1b5, 0x1c6, 0x1cd, 0x1de, 0x1ec, 0x1fa, 0x209, 0x21a, 0x21f, 0x22c, 0x230, 0x234, 0x238, 0x23a, 0x249, 0x24b, 0x24f} + +// nfkcDecompSparseValues: 605 entries, 2420 bytes +var nfkcDecompSparseValues = [605]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0002, lo: 0x0b}, + {value: 0x0001, lo: 0xa0, hi: 0xa0}, + {value: 0x0003, lo: 0xa8, hi: 0xa8}, + {value: 0x0007, lo: 0xaa, hi: 0xaa}, + {value: 0x0009, lo: 0xaf, hi: 0xaf}, + {value: 0x000d, lo: 0xb2, hi: 0xb4}, + {value: 0x0015, lo: 0xb5, hi: 0xb5}, + {value: 0x0018, lo: 0xb8, hi: 0xb8}, + {value: 0x001c, lo: 0xb9, hi: 0xba}, + {value: 0x0020, lo: 0xbc, hi: 0xbc}, + {value: 0x0026, lo: 0xbd, hi: 0xbd}, + {value: 0x002c, lo: 0xbe, hi: 0xbe}, + // Block 0x1, offset 0x2 + {value: 0x0004, lo: 0x09}, + {value: 0x0032, lo: 0x80, hi: 0x85}, + {value: 0x004a, lo: 0x87, hi: 0x8f}, + {value: 0x006e, lo: 0x91, hi: 0x96}, + {value: 0x0086, lo: 0x99, hi: 0x9d}, + {value: 0x009a, lo: 0xa0, hi: 0xa5}, + {value: 0x00b2, lo: 0xa7, hi: 0xaf}, + {value: 0x00d6, lo: 0xb1, hi: 0xb6}, + {value: 0x00ee, lo: 0xb9, hi: 0xbd}, + {value: 0x0102, lo: 0xbf, hi: 0xbf}, + // Block 0x2, offset 0x3 + {value: 0x0004, lo: 0x07}, + {value: 0x0106, lo: 0x80, hi: 0x8f}, + {value: 0x0146, lo: 0x92, hi: 0xa5}, + {value: 0x0196, lo: 0xa8, hi: 0xb0}, + {value: 0x01ba, lo: 0xb2, hi: 0xb2}, + {value: 0x01bd, lo: 0xb3, hi: 0xb3}, + {value: 0x01c0, lo: 0xb4, hi: 0xb7}, + {value: 0x01d0, lo: 0xb9, hi: 0xbf}, + // Block 0x3, offset 0x4 + {value: 0x0004, lo: 0x05}, + {value: 0x01ec, lo: 0x80, hi: 0x80}, + {value: 0x01f0, lo: 0x83, hi: 0x89}, + {value: 0x020c, lo: 0x8c, hi: 0x91}, + {value: 0x0224, lo: 0x94, hi: 0xa5}, + {value: 0x026c, lo: 0xa8, hi: 0xbf}, + // Block 0x4, offset 0x5 + {value: 0x0004, lo: 0x02}, + {value: 0x02ca, lo: 0xa0, hi: 0xa1}, + {value: 0x02d2, lo: 0xaf, hi: 0xb0}, + // Block 0x5, offset 0x6 + {value: 0x0004, lo: 0x09}, + {value: 0x03d8, lo: 0x80, hi: 0x9b}, + {value: 0x0448, lo: 0x9e, hi: 0x9f}, + {value: 0x0450, lo: 0xa6, hi: 0xaa}, + {value: 0x0466, lo: 0xab, hi: 0xab}, + {value: 0x046c, lo: 0xac, hi: 0xac}, + {value: 0x0472, lo: 0xad, hi: 0xad}, + {value: 0x0478, lo: 0xae, hi: 0xb0}, + {value: 0x0486, lo: 0xb1, hi: 0xb1}, + {value: 0x048c, lo: 0xb2, hi: 0xb3}, + // Block 0x6, offset 0x7 + {value: 0x0002, lo: 0x05}, + {value: 0x0494, lo: 0xb0, hi: 0xb1}, + {value: 0x0499, lo: 0xb2, hi: 0xb4}, + {value: 0x04a0, lo: 0xb5, hi: 0xb5}, + {value: 0x04a3, lo: 0xb6, hi: 0xb6}, + {value: 0x04a6, lo: 0xb7, hi: 0xb8}, + // Block 0x7, offset 0x8 + {value: 0x0004, lo: 0x06}, + {value: 0x04aa, lo: 0x98, hi: 0x9d}, + {value: 0x04c2, lo: 0xa0, hi: 0xa0}, + {value: 0x04c5, lo: 0xa1, hi: 0xa1}, + {value: 0x02c8, lo: 0xa2, hi: 0xa2}, + {value: 0x04c7, lo: 0xa3, hi: 0xa3}, + {value: 0x04c9, lo: 0xa4, hi: 0xa4}, + // Block 0x8, offset 0x9 + {value: 0x0003, lo: 0x05}, + {value: 0x04cc, lo: 0x80, hi: 0x81}, + {value: 0x04d2, lo: 0x83, hi: 0x84}, + {value: 0x04da, lo: 0xb4, hi: 0xb4}, + {value: 0x04dd, lo: 0xba, hi: 0xba}, + {value: 0x04e1, lo: 0xbe, hi: 0xbe}, + // Block 0x9, offset 0xa + {value: 0x0005, lo: 0x07}, + {value: 0x0011, lo: 0x84, hi: 0x84}, + {value: 0x04e8, lo: 0x85, hi: 0x85}, + {value: 0x04ee, lo: 0x86, hi: 0x87}, + {value: 0x04f6, lo: 0x88, hi: 0x8a}, + {value: 0x0505, lo: 0x8c, hi: 0x8c}, + {value: 0x050a, lo: 0x8e, hi: 0x90}, + {value: 0x051b, lo: 0xaa, hi: 0xb0}, + // Block 0xa, offset 0xb + {value: 0x0003, lo: 0x0c}, + {value: 0x0540, lo: 0x8a, hi: 0x8a}, + {value: 0x0545, lo: 0x8b, hi: 0x8b}, + {value: 0x054a, lo: 0x8c, hi: 0x8c}, + {value: 0x054f, lo: 0x8d, hi: 0x8d}, + {value: 0x0554, lo: 0x8e, hi: 0x8e}, + {value: 0x0559, lo: 0x90, hi: 0x92}, + {value: 0x050a, lo: 0x93, hi: 0x93}, + {value: 0x0520, lo: 0x94, hi: 0x94}, + {value: 0x056c, lo: 0x95, hi: 0x96}, + {value: 0x0572, lo: 0xb0, hi: 0xb2}, + {value: 0x057b, lo: 0xb4, hi: 0xb5}, + {value: 0x0581, lo: 0xb9, hi: 0xb9}, + // Block 0xb, offset 0xc + {value: 0x0005, lo: 0x06}, + {value: 0x0584, lo: 0x80, hi: 0x81}, + {value: 0x058e, lo: 0x83, hi: 0x83}, + {value: 0x0593, lo: 0x87, hi: 0x87}, + {value: 0x0598, lo: 0x8c, hi: 0x8e}, + {value: 0x05a7, lo: 0x99, hi: 0x99}, + {value: 0x05ac, lo: 0xb9, hi: 0xb9}, + // Block 0xc, offset 0xd + {value: 0x0005, lo: 0x05}, + {value: 0x05b1, lo: 0x90, hi: 0x91}, + {value: 0x05bb, lo: 0x93, hi: 0x93}, + {value: 0x05c0, lo: 0x97, hi: 0x97}, + {value: 0x05c5, lo: 0x9c, hi: 0x9e}, + {value: 0x05d4, lo: 0xb6, hi: 0xb7}, + // Block 0xd, offset 0xe + {value: 0x0005, lo: 0x07}, + {value: 0x05de, lo: 0x81, hi: 0x82}, + {value: 0x05e8, lo: 0x90, hi: 0x93}, + {value: 0x05fc, lo: 0x96, hi: 0x97}, + {value: 0x0606, lo: 0x9a, hi: 0x9f}, + {value: 0x0624, lo: 0xa2, hi: 0xa7}, + {value: 0x0642, lo: 0xaa, hi: 0xb5}, + {value: 0x067e, lo: 0xb8, hi: 0xb9}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x01}, + {value: 0x0688, lo: 0x87, hi: 0x87}, + // Block 0xf, offset 0x10 + {value: 0x0005, lo: 0x01}, + {value: 0x068d, lo: 0xa2, hi: 0xa6}, + // Block 0x10, offset 0x11 + {value: 0x0005, lo: 0x01}, + {value: 0x06a6, lo: 0xb5, hi: 0xb8}, + // Block 0x11, offset 0x12 + {value: 0x0005, lo: 0x03}, + {value: 0x06ba, lo: 0x80, hi: 0x80}, + {value: 0x06bf, lo: 0x82, hi: 0x82}, + {value: 0x06c4, lo: 0x93, hi: 0x93}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x03}, + {value: 0x06c9, lo: 0xa9, hi: 0xa9}, + {value: 0x06d0, lo: 0xb1, hi: 0xb1}, + {value: 0x06d7, lo: 0xb4, hi: 0xb4}, + // Block 0x13, offset 0x14 + {value: 0x0007, lo: 0x01}, + {value: 0x06de, lo: 0x98, hi: 0x9f}, + // Block 0x14, offset 0x15 + {value: 0x0007, lo: 0x03}, + {value: 0x0716, lo: 0x8b, hi: 0x8c}, + {value: 0x0724, lo: 0x9c, hi: 0x9d}, + {value: 0x0732, lo: 0x9f, hi: 0x9f}, + // Block 0x15, offset 0x16 + {value: 0x0007, lo: 0x02}, + {value: 0x0739, lo: 0xb3, hi: 0xb3}, + {value: 0x0740, lo: 0xb6, hi: 0xb6}, + // Block 0x16, offset 0x17 + {value: 0x0007, lo: 0x02}, + {value: 0x0747, lo: 0x99, hi: 0x9b}, + {value: 0x075c, lo: 0x9e, hi: 0x9e}, + // Block 0x17, offset 0x18 + {value: 0x0007, lo: 0x03}, + {value: 0x0763, lo: 0x88, hi: 0x88}, + {value: 0x076a, lo: 0x8b, hi: 0x8c}, + {value: 0x0778, lo: 0x9c, hi: 0x9d}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x01}, + {value: 0x0786, lo: 0x94, hi: 0x94}, + // Block 0x19, offset 0x1a + {value: 0x0007, lo: 0x01}, + {value: 0x078d, lo: 0x8a, hi: 0x8c}, + // Block 0x1a, offset 0x1b + {value: 0x0000, lo: 0x01}, + {value: 0x07a2, lo: 0x88, hi: 0x88}, + // Block 0x1b, offset 0x1c + {value: 0x0007, lo: 0x03}, + {value: 0x07a9, lo: 0x80, hi: 0x80}, + {value: 0x07b0, lo: 0x87, hi: 0x88}, + {value: 0x07be, lo: 0x8a, hi: 0x8b}, + // Block 0x1c, offset 0x1d + {value: 0x0007, lo: 0x01}, + {value: 0x07cf, lo: 0x8a, hi: 0x8c}, + // Block 0x1d, offset 0x1e + {value: 0x0007, lo: 0x03}, + {value: 0x07e4, lo: 0x9a, hi: 0x9a}, + {value: 0x07eb, lo: 0x9c, hi: 0x9d}, + {value: 0x07fc, lo: 0x9e, hi: 0x9e}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x01}, + {value: 0x0803, lo: 0xb3, hi: 0xb3}, + // Block 0x1f, offset 0x20 + {value: 0x0000, lo: 0x01}, + {value: 0x080a, lo: 0xb3, hi: 0xb3}, + // Block 0x20, offset 0x21 + {value: 0x0007, lo: 0x01}, + {value: 0x0811, lo: 0x9c, hi: 0x9d}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x01}, + {value: 0x081f, lo: 0x8c, hi: 0x8c}, + // Block 0x22, offset 0x23 + {value: 0x0007, lo: 0x09}, + {value: 0x0823, lo: 0x83, hi: 0x83}, + {value: 0x082a, lo: 0x8d, hi: 0x8d}, + {value: 0x0831, lo: 0x92, hi: 0x92}, + {value: 0x0838, lo: 0x97, hi: 0x97}, + {value: 0x083f, lo: 0x9c, hi: 0x9c}, + {value: 0x0846, lo: 0xa9, hi: 0xa9}, + {value: 0x084d, lo: 0xb3, hi: 0xb3}, + {value: 0x0854, lo: 0xb5, hi: 0xb7}, + {value: 0x086c, lo: 0xb8, hi: 0xb9}, + // Block 0x23, offset 0x24 + {value: 0x0007, lo: 0x07}, + {value: 0x087d, lo: 0x81, hi: 0x81}, + {value: 0x0884, lo: 0x93, hi: 0x93}, + {value: 0x088b, lo: 0x9d, hi: 0x9d}, + {value: 0x0892, lo: 0xa2, hi: 0xa2}, + {value: 0x0899, lo: 0xa7, hi: 0xa7}, + {value: 0x08a0, lo: 0xac, hi: 0xac}, + {value: 0x08a7, lo: 0xb9, hi: 0xb9}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x01}, + {value: 0x08ae, lo: 0xa6, hi: 0xa6}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x01}, + {value: 0x08b5, lo: 0xbc, hi: 0xbc}, + // Block 0x26, offset 0x27 + {value: 0x0007, lo: 0x08}, + {value: 0x08b9, lo: 0x86, hi: 0x86}, + {value: 0x08c0, lo: 0x88, hi: 0x88}, + {value: 0x08c7, lo: 0x8a, hi: 0x8a}, + {value: 0x08ce, lo: 0x8c, hi: 0x8c}, + {value: 0x08d5, lo: 0x8e, hi: 0x8e}, + {value: 0x08dc, lo: 0x92, hi: 0x92}, + {value: 0x08e3, lo: 0xbb, hi: 0xbb}, + {value: 0x08ea, lo: 0xbd, hi: 0xbd}, + // Block 0x27, offset 0x28 + {value: 0x0007, lo: 0x02}, + {value: 0x08f1, lo: 0x80, hi: 0x81}, + {value: 0x08ff, lo: 0x83, hi: 0x83}, + // Block 0x28, offset 0x29 + {value: 0x0002, lo: 0x06}, + {value: 0x0906, lo: 0xac, hi: 0xad}, + {value: 0x090b, lo: 0xae, hi: 0xae}, + {value: 0x090d, lo: 0xb0, hi: 0xb2}, + {value: 0x0914, lo: 0xb3, hi: 0xba}, + {value: 0x0924, lo: 0xbc, hi: 0xbd}, + {value: 0x0929, lo: 0xbe, hi: 0xbf}, + // Block 0x29, offset 0x2a + {value: 0x0003, lo: 0x0a}, + {value: 0x0981, lo: 0x9b, hi: 0x9c}, + {value: 0x0986, lo: 0x9d, hi: 0x9e}, + {value: 0x0949, lo: 0x9f, hi: 0x9f}, + {value: 0x098c, lo: 0xa0, hi: 0xa0}, + {value: 0x098e, lo: 0xa1, hi: 0xa7}, + {value: 0x09a4, lo: 0xa8, hi: 0xaa}, + {value: 0x09ae, lo: 0xab, hi: 0xb8}, + {value: 0x09d9, lo: 0xb9, hi: 0xbb}, + {value: 0x09e1, lo: 0xbc, hi: 0xbe}, + {value: 0x055c, lo: 0xbf, hi: 0xbf}, + // Block 0x2a, offset 0x2b + {value: 0x0004, lo: 0x0d}, + {value: 0x09ea, lo: 0x80, hi: 0x88}, + {value: 0x0a10, lo: 0x89, hi: 0x89}, + {value: 0x0a16, lo: 0x8a, hi: 0x94}, + {value: 0x0a44, lo: 0x95, hi: 0x95}, + {value: 0x0a4a, lo: 0x96, hi: 0x96}, + {value: 0x0a50, lo: 0x97, hi: 0x97}, + {value: 0x0a56, lo: 0x98, hi: 0x9c}, + {value: 0x0a6c, lo: 0x9d, hi: 0x9d}, + {value: 0x0a72, lo: 0x9e, hi: 0xae}, + {value: 0x0ab8, lo: 0xaf, hi: 0xaf}, + {value: 0x0abe, lo: 0xb0, hi: 0xb8}, + {value: 0x0ae4, lo: 0xb9, hi: 0xb9}, + {value: 0x0aea, lo: 0xba, hi: 0xbf}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x0d}, + {value: 0x0001, lo: 0x80, hi: 0x8a}, + {value: 0x1436, lo: 0x91, hi: 0x91}, + {value: 0x143a, lo: 0x97, hi: 0x97}, + {value: 0x143e, lo: 0xa4, hi: 0xa4}, + {value: 0x1440, lo: 0xa5, hi: 0xa5}, + {value: 0x1443, lo: 0xa6, hi: 0xa6}, + {value: 0x0001, lo: 0xaf, hi: 0xaf}, + {value: 0x1447, lo: 0xb3, hi: 0xb3}, + {value: 0x144e, lo: 0xb4, hi: 0xb4}, + {value: 0x1458, lo: 0xb6, hi: 0xb6}, + {value: 0x145f, lo: 0xb7, hi: 0xb7}, + {value: 0x1469, lo: 0xbc, hi: 0xbc}, + {value: 0x146c, lo: 0xbe, hi: 0xbe}, + // Block 0x2c, offset 0x2d + {value: 0x0002, lo: 0x09}, + {value: 0x1470, lo: 0x87, hi: 0x87}, + {value: 0x1473, lo: 0x88, hi: 0x88}, + {value: 0x1476, lo: 0x89, hi: 0x89}, + {value: 0x1479, lo: 0x97, hi: 0x97}, + {value: 0x0001, lo: 0x9f, hi: 0x9f}, + {value: 0x1486, lo: 0xb0, hi: 0xb0}, + {value: 0x097c, lo: 0xb1, hi: 0xb1}, + {value: 0x1488, lo: 0xb4, hi: 0xbb}, + {value: 0x149a, lo: 0xbc, hi: 0xbf}, + // Block 0x2d, offset 0x2e + {value: 0x0006, lo: 0x03}, + {value: 0x1599, lo: 0x89, hi: 0x89}, + {value: 0x159f, lo: 0x9a, hi: 0x9b}, + {value: 0x15ab, lo: 0xae, hi: 0xae}, + // Block 0x2e, offset 0x2f + {value: 0x0006, lo: 0x01}, + {value: 0x15b1, lo: 0x8d, hi: 0x8f}, + // Block 0x2f, offset 0x30 + {value: 0x0006, lo: 0x09}, + {value: 0x15c3, lo: 0x84, hi: 0x84}, + {value: 0x15c9, lo: 0x89, hi: 0x89}, + {value: 0x15cf, lo: 0x8c, hi: 0x8c}, + {value: 0x15d5, lo: 0xa4, hi: 0xa4}, + {value: 0x15db, lo: 0xa6, hi: 0xa6}, + {value: 0x15e1, lo: 0xac, hi: 0xac}, + {value: 0x15e8, lo: 0xad, hi: 0xad}, + {value: 0x15f2, lo: 0xaf, hi: 0xaf}, + {value: 0x15f9, lo: 0xb0, hi: 0xb0}, + // Block 0x30, offset 0x31 + {value: 0x0006, lo: 0x0b}, + {value: 0x1603, lo: 0x81, hi: 0x81}, + {value: 0x1609, lo: 0x84, hi: 0x84}, + {value: 0x160f, lo: 0x87, hi: 0x87}, + {value: 0x1615, lo: 0x89, hi: 0x89}, + {value: 0x161b, lo: 0xa0, hi: 0xa0}, + {value: 0x161f, lo: 0xa2, hi: 0xa2}, + {value: 0x1625, lo: 0xad, hi: 0xae}, + {value: 0x162f, lo: 0xaf, hi: 0xaf}, + {value: 0x1633, lo: 0xb0, hi: 0xb1}, + {value: 0x163f, lo: 0xb4, hi: 0xb5}, + {value: 0x164b, lo: 0xb8, hi: 0xb9}, + // Block 0x31, offset 0x32 + {value: 0x0006, lo: 0x04}, + {value: 0x1657, lo: 0x80, hi: 0x81}, + {value: 0x1663, lo: 0x84, hi: 0x85}, + {value: 0x166f, lo: 0x88, hi: 0x89}, + {value: 0x167b, lo: 0xac, hi: 0xaf}, + // Block 0x32, offset 0x33 + {value: 0x0006, lo: 0x02}, + {value: 0x1693, lo: 0xa0, hi: 0xa3}, + {value: 0x16ab, lo: 0xaa, hi: 0xad}, + // Block 0x33, offset 0x34 + {value: 0x0004, lo: 0x01}, + {value: 0x16c3, lo: 0xa9, hi: 0xaa}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x01}, + {value: 0x17fc, lo: 0x8c, hi: 0x8c}, + // Block 0x35, offset 0x36 + {value: 0x0004, lo: 0x02}, + {value: 0x1809, lo: 0xb4, hi: 0xb5}, + {value: 0x1810, lo: 0xb6, hi: 0xb6}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x1814, lo: 0x9c, hi: 0x9c}, + // Block 0x37, offset 0x38 + {value: 0x10c6, lo: 0x01}, + {value: 0x0499, lo: 0xbc, hi: 0xbd}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x01}, + {value: 0x181a, lo: 0xaf, hi: 0xaf}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x01}, + {value: 0x181e, lo: 0x9f, hi: 0x9f}, + // Block 0x3a, offset 0x3b + {value: 0x0000, lo: 0x01}, + {value: 0x1822, lo: 0xb3, hi: 0xb3}, + // Block 0x3b, offset 0x3c + {value: 0x0004, lo: 0x01}, + {value: 0x1826, lo: 0x80, hi: 0xbf}, + // Block 0x3c, offset 0x3d + {value: 0x0004, lo: 0x01}, + {value: 0x1926, lo: 0x80, hi: 0xbf}, + // Block 0x3d, offset 0x3e + {value: 0x0004, lo: 0x01}, + {value: 0x1a26, lo: 0x80, hi: 0xbf}, + // Block 0x3e, offset 0x3f + {value: 0x0004, lo: 0x01}, + {value: 0x1b26, lo: 0x80, hi: 0x95}, + // Block 0x3f, offset 0x40 + {value: 0x0300, lo: 0x04}, + {value: 0x0001, lo: 0x80, hi: 0x80}, + {value: 0x1b7e, lo: 0xb6, hi: 0xb6}, + {value: 0x1882, lo: 0xb8, hi: 0xb9}, + {value: 0x1b86, lo: 0xba, hi: 0xba}, + // Block 0x40, offset 0x41 + {value: 0x0007, lo: 0x0e}, + {value: 0x1c39, lo: 0x94, hi: 0x94}, + {value: 0x1c40, lo: 0x9b, hi: 0x9b}, + {value: 0x1c45, lo: 0x9c, hi: 0x9c}, + {value: 0x1c4a, lo: 0x9e, hi: 0x9f}, + {value: 0x1c58, lo: 0xac, hi: 0xac}, + {value: 0x1c5f, lo: 0xae, hi: 0xae}, + {value: 0x1c66, lo: 0xb0, hi: 0xb0}, + {value: 0x1c6d, lo: 0xb2, hi: 0xb2}, + {value: 0x1c74, lo: 0xb4, hi: 0xb4}, + {value: 0x1c7b, lo: 0xb6, hi: 0xb6}, + {value: 0x1c82, lo: 0xb8, hi: 0xb8}, + {value: 0x1c89, lo: 0xba, hi: 0xba}, + {value: 0x1c90, lo: 0xbc, hi: 0xbc}, + {value: 0x1c97, lo: 0xbe, hi: 0xbe}, + // Block 0x41, offset 0x42 + {value: 0x0007, lo: 0x0d}, + {value: 0x1c9e, lo: 0x80, hi: 0x80}, + {value: 0x1ca5, lo: 0x82, hi: 0x82}, + {value: 0x1cac, lo: 0x85, hi: 0x85}, + {value: 0x1cb3, lo: 0x87, hi: 0x87}, + {value: 0x1cba, lo: 0x89, hi: 0x89}, + {value: 0x1cc1, lo: 0x90, hi: 0x91}, + {value: 0x1ccf, lo: 0x93, hi: 0x94}, + {value: 0x1cdd, lo: 0x96, hi: 0x97}, + {value: 0x1ceb, lo: 0x99, hi: 0x9a}, + {value: 0x1cf9, lo: 0x9c, hi: 0x9d}, + {value: 0x1d07, lo: 0xb4, hi: 0xb4}, + {value: 0x1d0e, lo: 0xb7, hi: 0xba}, + {value: 0x1d2a, lo: 0xbe, hi: 0xbf}, + // Block 0x42, offset 0x43 + {value: 0x0004, lo: 0x01}, + {value: 0x1d38, lo: 0xb1, hi: 0xbf}, + // Block 0x43, offset 0x44 + {value: 0x0004, lo: 0x01}, + {value: 0x1d74, lo: 0x80, hi: 0xbf}, + // Block 0x44, offset 0x45 + {value: 0x0004, lo: 0x07}, + {value: 0x1e74, lo: 0x80, hi: 0x8e}, + {value: 0x1826, lo: 0x92, hi: 0x92}, + {value: 0x183e, lo: 0x93, hi: 0x93}, + {value: 0x1eb0, lo: 0x94, hi: 0x99}, + {value: 0x1836, lo: 0x9a, hi: 0x9a}, + {value: 0x1ec8, lo: 0x9b, hi: 0x9e}, + {value: 0x1846, lo: 0x9f, hi: 0x9f}, + // Block 0x45, offset 0x46 + {value: 0x0004, lo: 0x0f}, + {value: 0x221c, lo: 0x80, hi: 0x80}, + {value: 0x2221, lo: 0x81, hi: 0x81}, + {value: 0x2226, lo: 0x82, hi: 0x82}, + {value: 0x222b, lo: 0x83, hi: 0x83}, + {value: 0x2230, lo: 0x84, hi: 0x84}, + {value: 0x2235, lo: 0x85, hi: 0x85}, + {value: 0x223a, lo: 0x86, hi: 0x86}, + {value: 0x223f, lo: 0x87, hi: 0x87}, + {value: 0x2244, lo: 0x88, hi: 0x88}, + {value: 0x2249, lo: 0x89, hi: 0x89}, + {value: 0x224f, lo: 0x8a, hi: 0x8a}, + {value: 0x2255, lo: 0x8b, hi: 0x8b}, + {value: 0x225b, lo: 0x8c, hi: 0x8c}, + {value: 0x225e, lo: 0x8d, hi: 0x8e}, + {value: 0x2265, lo: 0x8f, hi: 0xbe}, + // Block 0x46, offset 0x47 + {value: 0x0000, lo: 0x01}, + {value: 0x2a7d, lo: 0xb0, hi: 0xb0}, + // Block 0x47, offset 0x48 + {value: 0x0004, lo: 0x0a}, + {value: 0x2a81, lo: 0x80, hi: 0x81}, + {value: 0x1a9e, lo: 0x82, hi: 0x82}, + {value: 0x2a89, lo: 0x83, hi: 0x86}, + {value: 0x1b76, lo: 0x87, hi: 0x87}, + {value: 0x1b76, lo: 0x88, hi: 0x88}, + {value: 0x2a99, lo: 0x89, hi: 0x89}, + {value: 0x1abe, lo: 0x8a, hi: 0x8a}, + {value: 0x2a9d, lo: 0x8b, hi: 0xb3}, + {value: 0x1a16, lo: 0xb4, hi: 0xb4}, + {value: 0x2b41, lo: 0xb5, hi: 0xbf}, + // Block 0x48, offset 0x49 + {value: 0x0004, lo: 0x06}, + {value: 0x1b3a, lo: 0x80, hi: 0x80}, + {value: 0x2b6d, lo: 0x81, hi: 0x9b}, + {value: 0x2ac1, lo: 0x9c, hi: 0x9c}, + {value: 0x2bd9, lo: 0x9d, hi: 0xb0}, + {value: 0x1aa6, lo: 0xb1, hi: 0xb1}, + {value: 0x2c29, lo: 0xb2, hi: 0xbf}, + // Block 0x49, offset 0x4a + {value: 0x0004, lo: 0x0a}, + {value: 0x2c61, lo: 0x80, hi: 0x80}, + {value: 0x18ba, lo: 0x81, hi: 0x81}, + {value: 0x2c65, lo: 0x82, hi: 0x89}, + {value: 0x186e, lo: 0x8a, hi: 0x8a}, + {value: 0x2c85, lo: 0x8b, hi: 0xa0}, + {value: 0x2c21, lo: 0xa1, hi: 0xa1}, + {value: 0x2cdd, lo: 0xa2, hi: 0xa9}, + {value: 0x2be1, lo: 0xaa, hi: 0xaa}, + {value: 0x2cfd, lo: 0xab, hi: 0xbe}, + {value: 0x2ac1, lo: 0xbf, hi: 0xbf}, + // Block 0x4a, offset 0x4b + {value: 0x0004, lo: 0x0b}, + {value: 0x2d4d, lo: 0x80, hi: 0x83}, + {value: 0x1b72, lo: 0x84, hi: 0x84}, + {value: 0x2d5d, lo: 0x85, hi: 0x90}, + {value: 0x2173, lo: 0x91, hi: 0x91}, + {value: 0x2d8d, lo: 0x92, hi: 0x9a}, + {value: 0x2be9, lo: 0x9b, hi: 0x9b}, + {value: 0x2db1, lo: 0x9c, hi: 0xa8}, + {value: 0x1aba, lo: 0xa9, hi: 0xa9}, + {value: 0x2de5, lo: 0xaa, hi: 0xb6}, + {value: 0x19f6, lo: 0xb7, hi: 0xb7}, + {value: 0x2e19, lo: 0xb8, hi: 0xbf}, + // Block 0x4b, offset 0x4c + {value: 0x0004, lo: 0x10}, + {value: 0x2e39, lo: 0x80, hi: 0x87}, + {value: 0x1a62, lo: 0x88, hi: 0x88}, + {value: 0x2e59, lo: 0x89, hi: 0x89}, + {value: 0x1a6e, lo: 0x8a, hi: 0x8a}, + {value: 0x2e5d, lo: 0x8b, hi: 0x8d}, + {value: 0x2e69, lo: 0x90, hi: 0x90}, + {value: 0x2e6d, lo: 0x92, hi: 0x92}, + {value: 0x2e71, lo: 0x95, hi: 0x9d}, + {value: 0x1a12, lo: 0x9e, hi: 0x9e}, + {value: 0x2e95, lo: 0xa0, hi: 0xa0}, + {value: 0x2e99, lo: 0xa2, hi: 0xa2}, + {value: 0x2e9d, lo: 0xa5, hi: 0xa6}, + {value: 0x2ea5, lo: 0xaa, hi: 0xad}, + {value: 0x2eb5, lo: 0xb0, hi: 0xbb}, + {value: 0x18d6, lo: 0xbc, hi: 0xbc}, + {value: 0x2ee5, lo: 0xbd, hi: 0xbf}, + // Block 0x4c, offset 0x4d + {value: 0x0004, lo: 0x10}, + {value: 0x2ef1, lo: 0x80, hi: 0x8b}, + {value: 0x2187, lo: 0x8c, hi: 0x8c}, + {value: 0x2f21, lo: 0x8d, hi: 0x90}, + {value: 0x2197, lo: 0x91, hi: 0x91}, + {value: 0x2f31, lo: 0x92, hi: 0x96}, + {value: 0x2cb1, lo: 0x97, hi: 0x97}, + {value: 0x2f45, lo: 0x98, hi: 0x9d}, + {value: 0x2f59, lo: 0x9e, hi: 0xa6}, + {value: 0x2e9d, lo: 0xa7, hi: 0xa7}, + {value: 0x2f7d, lo: 0xa8, hi: 0xac}, + {value: 0x2f92, lo: 0xad, hi: 0xad}, + {value: 0x2f96, lo: 0xb0, hi: 0xb7}, + {value: 0x2ecd, lo: 0xb8, hi: 0xb8}, + {value: 0x2fb6, lo: 0xb9, hi: 0xbb}, + {value: 0x2e69, lo: 0xbc, hi: 0xbc}, + {value: 0x2fc2, lo: 0xbd, hi: 0xbf}, + // Block 0x4d, offset 0x4e + {value: 0x0005, lo: 0x06}, + {value: 0x3277, lo: 0x80, hi: 0x80}, + {value: 0x327e, lo: 0x81, hi: 0x81}, + {value: 0x3285, lo: 0x82, hi: 0x82}, + {value: 0x326d, lo: 0x83, hi: 0x83}, + {value: 0x328c, lo: 0x84, hi: 0x84}, + {value: 0x3293, lo: 0x85, hi: 0xbf}, + // Block 0x4e, offset 0x4f + {value: 0x0005, lo: 0x10}, + {value: 0x356a, lo: 0x80, hi: 0x8b}, + {value: 0x3514, lo: 0x8c, hi: 0x8c}, + {value: 0x35a6, lo: 0x8d, hi: 0x90}, + {value: 0x3533, lo: 0x91, hi: 0xa7}, + {value: 0x3514, lo: 0xa8, hi: 0xa8}, + {value: 0x35a6, lo: 0xa9, hi: 0xac}, + {value: 0x3597, lo: 0xad, hi: 0xaf}, + {value: 0x3514, lo: 0xb0, hi: 0xb0}, + {value: 0x350f, lo: 0xb1, hi: 0xb1}, + {value: 0x3519, lo: 0xb2, hi: 0xb2}, + {value: 0x333d, lo: 0xb3, hi: 0xb3}, + {value: 0x3306, lo: 0xb4, hi: 0xb6}, + {value: 0x3597, lo: 0xb7, hi: 0xb9}, + {value: 0x333d, lo: 0xba, hi: 0xbb}, + {value: 0x35ba, lo: 0xbc, hi: 0xbc}, + {value: 0x35ba, lo: 0xbd, hi: 0xbd}, + // Block 0x4f, offset 0x50 + {value: 0x0007, lo: 0x0d}, + {value: 0x35bf, lo: 0x90, hi: 0x91}, + {value: 0x35c6, lo: 0x92, hi: 0x98}, + {value: 0x35f0, lo: 0x99, hi: 0x9f}, + {value: 0x361a, lo: 0xa0, hi: 0xa2}, + {value: 0x3628, lo: 0xa3, hi: 0xa4}, + {value: 0x362f, lo: 0xa5, hi: 0xa7}, + {value: 0x363d, lo: 0xa8, hi: 0xaa}, + {value: 0x364b, lo: 0xab, hi: 0xac}, + {value: 0x3652, lo: 0xad, hi: 0xaf}, + {value: 0x3660, lo: 0xb0, hi: 0xb1}, + {value: 0x3667, lo: 0xb2, hi: 0xb6}, + {value: 0x3683, lo: 0xb7, hi: 0xbc}, + {value: 0x36a6, lo: 0xbd, hi: 0xbf}, + // Block 0x50, offset 0x51 + {value: 0x0007, lo: 0x0d}, + {value: 0x36bb, lo: 0x80, hi: 0x83}, + {value: 0x36d0, lo: 0x84, hi: 0x85}, + {value: 0x36d7, lo: 0x86, hi: 0x87}, + {value: 0x36de, lo: 0x88, hi: 0x8f}, + {value: 0x3716, lo: 0x92, hi: 0x97}, + {value: 0x3739, lo: 0x98, hi: 0x9c}, + {value: 0x3755, lo: 0x9d, hi: 0xb3}, + {value: 0x36ad, lo: 0xb4, hi: 0xb4}, + {value: 0x36bb, lo: 0xb5, hi: 0xb5}, + {value: 0x37f6, lo: 0xb6, hi: 0xbb}, + {value: 0x3812, lo: 0xbc, hi: 0xbc}, + {value: 0x3804, lo: 0xbd, hi: 0xbd}, + {value: 0x3820, lo: 0xbe, hi: 0xbf}, + // Block 0x51, offset 0x52 + {value: 0x0009, lo: 0x0e}, + {value: 0x382e, lo: 0x80, hi: 0x80}, + {value: 0x3835, lo: 0x81, hi: 0x81}, + {value: 0x383c, lo: 0x82, hi: 0x82}, + {value: 0x3819, lo: 0x83, hi: 0x83}, + {value: 0x367c, lo: 0x84, hi: 0x84}, + {value: 0x3636, lo: 0x85, hi: 0x85}, + {value: 0x3843, lo: 0x86, hi: 0x86}, + {value: 0x384a, lo: 0x87, hi: 0x87}, + {value: 0x3851, lo: 0xb0, hi: 0xb0}, + {value: 0x3858, lo: 0xb1, hi: 0xb1}, + {value: 0x385f, lo: 0xb2, hi: 0xb9}, + {value: 0x38a5, lo: 0xba, hi: 0xba}, + {value: 0x38c7, lo: 0xbb, hi: 0xbb}, + {value: 0x38d7, lo: 0xbc, hi: 0xbc}, + // Block 0x52, offset 0x53 + {value: 0x0004, lo: 0x10}, + {value: 0x38e0, lo: 0x90, hi: 0x90}, + {value: 0x38e2, lo: 0x91, hi: 0x93}, + {value: 0x04e1, lo: 0x94, hi: 0x94}, + {value: 0x38ec, lo: 0x95, hi: 0x95}, + {value: 0x38ee, lo: 0x96, hi: 0x96}, + {value: 0x38f0, lo: 0x97, hi: 0x98}, + {value: 0x1443, lo: 0x99, hi: 0x99}, + {value: 0x1440, lo: 0xb0, hi: 0xb0}, + {value: 0x38f8, lo: 0xb1, hi: 0xb3}, + {value: 0x3900, lo: 0xb4, hi: 0xb4}, + {value: 0x149c, lo: 0xb5, hi: 0xb5}, + {value: 0x149e, lo: 0xb6, hi: 0xb6}, + {value: 0x3902, lo: 0xb7, hi: 0xb7}, + {value: 0x3904, lo: 0xb8, hi: 0xb8}, + {value: 0x3906, lo: 0xb9, hi: 0xbe}, + {value: 0x16c3, lo: 0xbf, hi: 0xbf}, + // Block 0x53, offset 0x54 + {value: 0x0004, lo: 0x04}, + {value: 0x22a5, lo: 0x80, hi: 0x9c}, + {value: 0x3a38, lo: 0x9d, hi: 0x9f}, + {value: 0x1e04, lo: 0xa0, hi: 0xa0}, + {value: 0x1d38, lo: 0xa1, hi: 0xbe}, + // Block 0x54, offset 0x55 + {value: 0x0004, lo: 0x0c}, + {value: 0x1db0, lo: 0x82, hi: 0x87}, + {value: 0x1dc8, lo: 0x8a, hi: 0x8f}, + {value: 0x1de0, lo: 0x92, hi: 0x97}, + {value: 0x1df8, lo: 0x9a, hi: 0x9c}, + {value: 0x3a44, lo: 0xa0, hi: 0xa0}, + {value: 0x3a47, lo: 0xa1, hi: 0xa1}, + {value: 0x3a4a, lo: 0xa2, hi: 0xa2}, + {value: 0x0009, lo: 0xa3, hi: 0xa3}, + {value: 0x3a4d, lo: 0xa4, hi: 0xa4}, + {value: 0x3a50, lo: 0xa5, hi: 0xa5}, + {value: 0x3a53, lo: 0xa6, hi: 0xa6}, + {value: 0x3a57, lo: 0xa8, hi: 0xae}, + // Block 0x55, offset 0x56 + {value: 0x0009, lo: 0x03}, + {value: 0x3a73, lo: 0x9a, hi: 0x9a}, + {value: 0x3a7c, lo: 0x9c, hi: 0x9c}, + {value: 0x3a85, lo: 0xab, hi: 0xab}, + // Block 0x56, offset 0x57 + {value: 0x000d, lo: 0x03}, + {value: 0x3a8e, lo: 0x9e, hi: 0x9e}, + {value: 0x3a97, lo: 0x9f, hi: 0x9f}, + {value: 0x3aa0, lo: 0xa0, hi: 0xa4}, + // Block 0x57, offset 0x58 + {value: 0x0009, lo: 0x03}, + {value: 0x3ae1, lo: 0xbb, hi: 0xbd}, + {value: 0x3b00, lo: 0xbe, hi: 0xbe}, + {value: 0x3b0d, lo: 0xbf, hi: 0xbf}, + // Block 0x58, offset 0x59 + {value: 0x0000, lo: 0x01}, + {value: 0x3b1a, lo: 0x80, hi: 0x80}, + // Block 0x59, offset 0x5a + {value: 0x0003, lo: 0x0e}, + {value: 0x14c8, lo: 0x80, hi: 0x80}, + {value: 0x092b, lo: 0x81, hi: 0x81}, + {value: 0x17f6, lo: 0x82, hi: 0x82}, + {value: 0x092d, lo: 0x83, hi: 0x83}, + {value: 0x092f, lo: 0x84, hi: 0x84}, + {value: 0x155f, lo: 0x85, hi: 0x85}, + {value: 0x0931, lo: 0x86, hi: 0x86}, + {value: 0x1570, lo: 0x87, hi: 0x87}, + {value: 0x17f8, lo: 0x88, hi: 0x88}, + {value: 0x14d4, lo: 0x89, hi: 0x89}, + {value: 0x3c2c, lo: 0x8a, hi: 0x8a}, + {value: 0x293d, lo: 0x8b, hi: 0x8b}, + {value: 0x3c2f, lo: 0x8c, hi: 0x8e}, + {value: 0x3c39, lo: 0x8f, hi: 0x8f}, + // Block 0x5a, offset 0x5b + {value: 0x0000, lo: 0x01}, + {value: 0x3c3c, lo: 0x90, hi: 0x90}, + // Block 0x5b, offset 0x5c + {value: 0x000a, lo: 0x03}, + {value: 0x3cc1, lo: 0x80, hi: 0x88}, + {value: 0x3d1b, lo: 0x90, hi: 0x90}, + {value: 0x3d1f, lo: 0x91, hi: 0x91}, + // Block 0x5c, offset 0x5d + {value: 0x0004, lo: 0x0d}, + {value: 0x4463, lo: 0x80, hi: 0x81}, + {value: 0x446c, lo: 0x82, hi: 0x89}, + {value: 0x30a2, lo: 0x8a, hi: 0x8a}, + {value: 0x448d, lo: 0x8b, hi: 0x90}, + {value: 0x44a6, lo: 0x91, hi: 0x92}, + {value: 0x44af, lo: 0x93, hi: 0x93}, + {value: 0x44b4, lo: 0x94, hi: 0x94}, + {value: 0x1b42, lo: 0x95, hi: 0x95}, + {value: 0x44b9, lo: 0x96, hi: 0x96}, + {value: 0x1b52, lo: 0x97, hi: 0x97}, + {value: 0x44bd, lo: 0x98, hi: 0x9b}, + {value: 0x1b66, lo: 0x9c, hi: 0x9c}, + {value: 0x44cd, lo: 0x9d, hi: 0x9d}, } // nfkcDecompLookup: 960 bytes @@ -4367,51 +4608,51 @@ var nfkcDecompLookup = [960]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c2: 0x03, 0x0c3: 0x04, 0x0c4: 0x05, 0x0c5: 0x06, 0x0c6: 0x07, 0x0c7: 0x08, - 0x0c8: 0x09, 0x0ca: 0x0a, 0x0cb: 0x0b, 0x0cd: 0x0c, 0x0ce: 0x0d, 0x0cf: 0x0e, - 0x0d0: 0x0f, 0x0d1: 0x10, 0x0d3: 0x11, 0x0d6: 0x12, - 0x0d8: 0x13, 0x0d9: 0x14, 0x0db: 0x15, + 0x0c2: 0x42, 0x0c3: 0x43, 0x0c4: 0x44, 0x0c5: 0x45, 0x0c6: 0x46, 0x0c7: 0x03, + 0x0c8: 0x47, 0x0ca: 0x48, 0x0cb: 0x49, 0x0cd: 0x4a, 0x0ce: 0x4b, 0x0cf: 0x4c, + 0x0d0: 0x4d, 0x0d1: 0x4e, 0x0d3: 0x4f, 0x0d6: 0x50, + 0x0d8: 0x51, 0x0d9: 0x52, 0x0db: 0x53, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ea: 0x08, 0x0ef: 0x09, 0x0f0: 0x0e, // Block 0x4, offset 0x100 - 0x124: 0x16, 0x125: 0x17, 0x127: 0x18, - 0x128: 0x19, 0x129: 0x1a, 0x12d: 0x1b, 0x12e: 0x1c, 0x12f: 0x1d, - 0x131: 0x1e, 0x133: 0x1f, 0x135: 0x20, 0x137: 0x21, - 0x138: 0x22, 0x13a: 0x23, 0x13b: 0x24, 0x13c: 0x25, 0x13d: 0x26, 0x13e: 0x27, + 0x124: 0x54, 0x125: 0x55, 0x127: 0x56, + 0x128: 0x57, 0x129: 0x58, 0x12d: 0x59, 0x12e: 0x5a, 0x12f: 0x5b, + 0x131: 0x5c, 0x133: 0x5d, 0x135: 0x5e, 0x137: 0x5f, + 0x138: 0x60, 0x13a: 0x61, 0x13b: 0x62, 0x13c: 0x63, 0x13d: 0x64, 0x13e: 0x65, // Block 0x5, offset 0x140 - 0x140: 0x28, 0x143: 0x29, - 0x16c: 0x2a, 0x16d: 0x2b, - 0x174: 0x2c, 0x175: 0x2d, 0x176: 0x2e, - 0x178: 0x2f, 0x179: 0x30, 0x17a: 0x31, 0x17b: 0x32, 0x17c: 0x33, 0x17d: 0x34, 0x17e: 0x35, 0x17f: 0x36, + 0x140: 0x66, 0x143: 0x67, + 0x16c: 0x68, 0x16d: 0x69, + 0x174: 0x6a, 0x175: 0x04, 0x176: 0x6b, + 0x178: 0x6c, 0x179: 0x05, 0x17a: 0x06, 0x17b: 0x07, 0x17c: 0x08, 0x17d: 0x09, 0x17e: 0x0a, 0x17f: 0x0b, // Block 0x6, offset 0x180 - 0x180: 0x37, 0x181: 0x38, 0x182: 0x39, 0x184: 0x3a, 0x185: 0x3b, 0x186: 0x3c, 0x187: 0x3d, - 0x188: 0x3e, 0x189: 0x3f, 0x18a: 0x40, 0x18b: 0x41, 0x18c: 0x42, - 0x191: 0x43, 0x192: 0x44, 0x193: 0x45, - 0x1a8: 0x46, 0x1a9: 0x47, 0x1ab: 0x48, - 0x1b1: 0x49, 0x1b5: 0x4a, - 0x1ba: 0x4b, 0x1bb: 0x4c, 0x1bc: 0x4d, 0x1bd: 0x4e, 0x1be: 0x4f, 0x1bf: 0x50, + 0x180: 0x6d, 0x181: 0x6e, 0x182: 0x0c, 0x184: 0x0d, 0x185: 0x0e, 0x186: 0x6f, 0x187: 0x70, + 0x188: 0x71, 0x189: 0x72, 0x18a: 0x73, 0x18b: 0x74, 0x18c: 0x75, + 0x191: 0x0f, 0x192: 0x10, 0x193: 0x11, + 0x1a8: 0x76, 0x1a9: 0x77, 0x1ab: 0x78, + 0x1b1: 0x79, 0x1b5: 0x7a, + 0x1ba: 0x7b, 0x1bb: 0x7c, 0x1bc: 0x7d, 0x1bd: 0x7e, 0x1be: 0x7f, 0x1bf: 0x80, // Block 0x7, offset 0x1c0 - 0x1c0: 0x51, 0x1c1: 0x52, 0x1c2: 0x53, 0x1c3: 0x54, 0x1c4: 0x55, 0x1c5: 0x56, 0x1c6: 0x57, - 0x1c8: 0x58, 0x1c9: 0x59, 0x1ca: 0x5a, 0x1cb: 0x5b, 0x1cc: 0x5c, 0x1cd: 0x5d, 0x1ce: 0x5e, 0x1cf: 0x5f, + 0x1c0: 0x81, 0x1c1: 0x12, 0x1c2: 0x82, 0x1c3: 0x83, 0x1c4: 0x84, 0x1c5: 0x85, 0x1c6: 0x86, + 0x1c8: 0x13, 0x1c9: 0x14, 0x1ca: 0x15, 0x1cb: 0x87, 0x1cc: 0x16, 0x1cd: 0x17, 0x1ce: 0x18, 0x1cf: 0x19, // Block 0x8, offset 0x200 - 0x21d: 0x60, + 0x21d: 0x88, // Block 0x9, offset 0x240 - 0x264: 0x61, 0x265: 0x62, 0x266: 0x63, 0x267: 0x64, - 0x268: 0x65, 0x269: 0x66, 0x26a: 0x67, 0x26b: 0x68, 0x26c: 0x69, 0x26d: 0x6a, 0x26e: 0x6b, 0x26f: 0x6c, - 0x270: 0x6d, 0x271: 0x6e, 0x272: 0x6f, 0x273: 0x70, 0x274: 0x71, 0x275: 0x72, 0x276: 0x73, 0x277: 0x74, - 0x278: 0x75, 0x279: 0x76, 0x27a: 0x77, 0x27b: 0x78, 0x27c: 0x79, 0x27d: 0x7a, 0x27e: 0x7b, 0x27f: 0x7c, + 0x264: 0x89, 0x265: 0x8a, 0x266: 0x8b, 0x267: 0x8c, + 0x268: 0x8d, 0x269: 0x8e, 0x26a: 0x1a, 0x26b: 0x1b, 0x26c: 0x1c, 0x26d: 0x1d, 0x26e: 0x1e, 0x26f: 0x1f, + 0x270: 0x8f, 0x271: 0x20, 0x272: 0x21, 0x273: 0x22, 0x274: 0x90, 0x275: 0x91, 0x276: 0x92, 0x277: 0x93, + 0x278: 0x94, 0x279: 0x23, 0x27a: 0x24, 0x27b: 0x25, 0x27c: 0x26, 0x27d: 0x27, 0x27e: 0x95, 0x27f: 0x96, // Block 0xa, offset 0x280 - 0x282: 0x7d, + 0x282: 0x97, // Block 0xb, offset 0x2c0 - 0x2c5: 0x7e, 0x2c6: 0x7f, 0x2c7: 0x80, - 0x2d0: 0x81, 0x2d1: 0x82, 0x2d2: 0x83, 0x2d3: 0x84, 0x2d4: 0x85, 0x2d5: 0x86, 0x2d6: 0x87, 0x2d7: 0x88, - 0x2d8: 0x89, 0x2d9: 0x8a, 0x2da: 0x8b, 0x2db: 0x8c, 0x2dc: 0x8d, 0x2dd: 0x8e, 0x2de: 0x8f, 0x2df: 0x90, + 0x2c5: 0x98, 0x2c6: 0x99, 0x2c7: 0x9a, + 0x2d0: 0x28, 0x2d1: 0x29, 0x2d2: 0x2a, 0x2d3: 0x2b, 0x2d4: 0x2c, 0x2d5: 0x2d, 0x2d6: 0x2e, 0x2d7: 0x2f, + 0x2d8: 0x30, 0x2d9: 0x31, 0x2da: 0x32, 0x2db: 0x33, 0x2dc: 0x34, 0x2dd: 0x35, 0x2de: 0x36, 0x2df: 0x37, // Block 0xc, offset 0x300 - 0x304: 0x91, 0x305: 0x92, 0x306: 0x93, - 0x308: 0x94, 0x309: 0x95, + 0x304: 0x38, 0x305: 0x9b, 0x306: 0x9c, + 0x308: 0x39, 0x309: 0x9d, // Block 0xd, offset 0x340 - 0x360: 0x96, 0x361: 0x97, 0x362: 0x98, 0x363: 0x99, 0x364: 0x9a, 0x365: 0x9b, 0x366: 0x9c, 0x367: 0x9d, + 0x360: 0x3a, 0x361: 0x3b, 0x362: 0x3c, 0x363: 0x3d, 0x364: 0x3e, 0x365: 0x3f, 0x366: 0x40, 0x367: 0x41, 0x368: 0x9e, // Block 0xe, offset 0x380 0x391: 0x0a, @@ -4419,7 +4660,7 @@ var nfkcDecompLookup = [960]uint8{ 0x3af: 0x0d, } -var nfkcDecompTrie = trie{nfkcDecompLookup[:], nfkcDecompValues[:]} +var nfkcDecompTrie = trie{nfkcDecompLookup[:], nfkcDecompValues[:], nfkcDecompSparseValues[:], nfkcDecompSparseOffset[:], 66} // recompMap: 7448 bytes (entries only) var recompMap = map[uint32]uint32{ @@ -5356,9 +5597,9 @@ var recompMap = map[uint32]uint32{ 0x10A510BA: 0x110AB, } -// charInfoValues: 11008 entries, 22016 bytes +// charInfoValues: 1024 entries, 2048 bytes // Block 2 is the null block. -var charInfoValues = [11008]uint16{ +var charInfoValues = [1024]uint16{ // Block 0x0, offset 0x0 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800, // Block 0x1, offset 0x40 @@ -5374,1134 +5615,1071 @@ var charInfoValues = [11008]uint16{ 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800, // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x00e0: 0x3000, - 0x00e8: 0x3800, - 0x00ea: 0x3000, 0x00ef: 0x3000, - 0x00f2: 0x3000, 0x00f3: 0x3000, 0x00f4: 0x3000, 0x00f5: 0x3000, - 0x00f8: 0x3000, 0x00f9: 0x3000, 0x00fa: 0x3000, - 0x00fc: 0x3000, 0x00fd: 0x3000, 0x00fe: 0x3000, + 0x00c0: 0x1100, 0x00c1: 0x1100, 0x00c2: 0x9900, 0x00c3: 0x1100, 0x00c4: 0x9900, 0x00c5: 0x9900, + 0x00c6: 0x8800, 0x00c7: 0x9900, 0x00c8: 0x1100, 0x00c9: 0x1100, 0x00ca: 0x9900, 0x00cb: 0x1100, + 0x00cc: 0x1100, 0x00cd: 0x1100, 0x00ce: 0x1100, 0x00cf: 0x9900, 0x00d1: 0x1100, + 0x00d2: 0x1100, 0x00d3: 0x1100, 0x00d4: 0x9900, 0x00d5: 0x9900, 0x00d6: 0x9900, + 0x00d8: 0x8800, 0x00d9: 0x1100, 0x00da: 0x1100, 0x00db: 0x1100, 0x00dc: 0x9900, 0x00dd: 0x1100, + 0x00e0: 0x1100, 0x00e1: 0x1100, 0x00e2: 0x9900, 0x00e3: 0x1100, + 0x00e4: 0x9900, 0x00e5: 0x9900, 0x00e6: 0x8800, 0x00e7: 0x9900, 0x00e8: 0x1100, 0x00e9: 0x1100, + 0x00ea: 0x9900, 0x00eb: 0x1100, 0x00ec: 0x1100, 0x00ed: 0x1100, 0x00ee: 0x1100, 0x00ef: 0x9900, + 0x00f1: 0x1100, 0x00f2: 0x1100, 0x00f3: 0x1100, 0x00f4: 0x9900, 0x00f5: 0x9900, + 0x00f6: 0x9900, 0x00f8: 0x8800, 0x00f9: 0x1100, 0x00fa: 0x1100, 0x00fb: 0x1100, + 0x00fc: 0x9900, 0x00fd: 0x1100, 0x00ff: 0x1100, // Block 0x4, offset 0x100 - 0x0100: 0x1100, 0x0101: 0x1100, 0x0102: 0x9900, 0x0103: 0x1100, 0x0104: 0x9900, 0x0105: 0x9900, - 0x0106: 0x8800, 0x0107: 0x9900, 0x0108: 0x1100, 0x0109: 0x1100, 0x010a: 0x9900, 0x010b: 0x1100, - 0x010c: 0x1100, 0x010d: 0x1100, 0x010e: 0x1100, 0x010f: 0x9900, 0x0111: 0x1100, - 0x0112: 0x1100, 0x0113: 0x1100, 0x0114: 0x9900, 0x0115: 0x9900, 0x0116: 0x9900, - 0x0118: 0x8800, 0x0119: 0x1100, 0x011a: 0x1100, 0x011b: 0x1100, 0x011c: 0x9900, 0x011d: 0x1100, - 0x0120: 0x1100, 0x0121: 0x1100, 0x0122: 0x9900, 0x0123: 0x1100, - 0x0124: 0x9900, 0x0125: 0x9900, 0x0126: 0x8800, 0x0127: 0x9900, 0x0128: 0x1100, 0x0129: 0x1100, - 0x012a: 0x9900, 0x012b: 0x1100, 0x012c: 0x1100, 0x012d: 0x1100, 0x012e: 0x1100, 0x012f: 0x9900, - 0x0131: 0x1100, 0x0132: 0x1100, 0x0133: 0x1100, 0x0134: 0x9900, 0x0135: 0x9900, - 0x0136: 0x9900, 0x0138: 0x8800, 0x0139: 0x1100, 0x013a: 0x1100, 0x013b: 0x1100, - 0x013c: 0x9900, 0x013d: 0x1100, 0x013f: 0x1100, + 0x0100: 0x66e6, 0x0101: 0x66e6, 0x0102: 0x66e6, 0x0103: 0x66e6, 0x0104: 0x66e6, 0x0105: 0x00e6, + 0x0106: 0x66e6, 0x0107: 0x66e6, 0x0108: 0x66e6, 0x0109: 0x66e6, 0x010a: 0x66e6, 0x010b: 0x66e6, + 0x010c: 0x66e6, 0x010d: 0x00e6, 0x010e: 0x00e6, 0x010f: 0x66e6, 0x0110: 0x00e6, 0x0111: 0x66e6, + 0x0112: 0x00e6, 0x0113: 0x66e6, 0x0114: 0x66e6, 0x0115: 0x00e8, 0x0116: 0x00dc, 0x0117: 0x00dc, + 0x0118: 0x00dc, 0x0119: 0x00dc, 0x011a: 0x00e8, 0x011b: 0x66d8, 0x011c: 0x00dc, 0x011d: 0x00dc, + 0x011e: 0x00dc, 0x011f: 0x00dc, 0x0120: 0x00dc, 0x0121: 0x00ca, 0x0122: 0x00ca, 0x0123: 0x66dc, + 0x0124: 0x66dc, 0x0125: 0x66dc, 0x0126: 0x66dc, 0x0127: 0x66ca, 0x0128: 0x66ca, 0x0129: 0x00dc, + 0x012a: 0x00dc, 0x012b: 0x00dc, 0x012c: 0x00dc, 0x012d: 0x66dc, 0x012e: 0x66dc, 0x012f: 0x00dc, + 0x0130: 0x66dc, 0x0131: 0x66dc, 0x0132: 0x00dc, 0x0133: 0x00dc, 0x0134: 0x0001, 0x0135: 0x0001, + 0x0136: 0x0001, 0x0137: 0x0001, 0x0138: 0x6601, 0x0139: 0x00dc, 0x013a: 0x00dc, 0x013b: 0x00dc, + 0x013c: 0x00dc, 0x013d: 0x00e6, 0x013e: 0x00e6, 0x013f: 0x00e6, // Block 0x5, offset 0x140 - 0x0140: 0x1100, 0x0141: 0x1100, 0x0142: 0x9900, 0x0143: 0x9900, 0x0144: 0x1100, 0x0145: 0x1100, - 0x0146: 0x1100, 0x0147: 0x1100, 0x0148: 0x1100, 0x0149: 0x1100, 0x014a: 0x1100, 0x014b: 0x1100, - 0x014c: 0x1100, 0x014d: 0x1100, 0x014e: 0x1100, 0x014f: 0x1100, - 0x0152: 0x9900, 0x0153: 0x9900, 0x0154: 0x1100, 0x0155: 0x1100, 0x0156: 0x1100, 0x0157: 0x1100, - 0x0158: 0x1100, 0x0159: 0x1100, 0x015a: 0x1100, 0x015b: 0x1100, 0x015c: 0x1100, 0x015d: 0x1100, - 0x015e: 0x1100, 0x015f: 0x1100, 0x0160: 0x1100, 0x0161: 0x1100, 0x0162: 0x1100, 0x0163: 0x1100, - 0x0164: 0x1100, 0x0165: 0x1100, 0x0168: 0x1100, 0x0169: 0x1100, - 0x016a: 0x1100, 0x016b: 0x1100, 0x016c: 0x1100, 0x016d: 0x1100, 0x016e: 0x1100, 0x016f: 0x1100, - 0x0170: 0x1100, 0x0172: 0x3000, 0x0173: 0x3000, 0x0174: 0x1100, 0x0175: 0x1100, - 0x0176: 0x1100, 0x0177: 0x1100, 0x0179: 0x1100, 0x017a: 0x1100, 0x017b: 0x1100, - 0x017c: 0x1100, 0x017d: 0x1100, 0x017e: 0x1100, 0x017f: 0x3000, + 0x0140: 0x33e6, 0x0141: 0x33e6, 0x0142: 0x66e6, 0x0143: 0x33e6, 0x0144: 0x33e6, 0x0145: 0x66f0, + 0x0146: 0x00e6, 0x0147: 0x00dc, 0x0148: 0x00dc, 0x0149: 0x00dc, 0x014a: 0x00e6, 0x014b: 0x00e6, + 0x014c: 0x00e6, 0x014d: 0x00dc, 0x014e: 0x00dc, 0x0150: 0x00e6, 0x0151: 0x00e6, + 0x0152: 0x00e6, 0x0153: 0x00dc, 0x0154: 0x00dc, 0x0155: 0x00dc, 0x0156: 0x00dc, 0x0157: 0x00e6, + 0x0158: 0x00e8, 0x0159: 0x00dc, 0x015a: 0x00dc, 0x015b: 0x00e6, 0x015c: 0x00e9, 0x015d: 0x00ea, + 0x015e: 0x00ea, 0x015f: 0x00e9, 0x0160: 0x00ea, 0x0161: 0x00ea, 0x0162: 0x00e9, 0x0163: 0x00e6, + 0x0164: 0x00e6, 0x0165: 0x00e6, 0x0166: 0x00e6, 0x0167: 0x00e6, 0x0168: 0x00e6, 0x0169: 0x00e6, + 0x016a: 0x00e6, 0x016b: 0x00e6, 0x016c: 0x00e6, 0x016d: 0x00e6, 0x016e: 0x00e6, 0x016f: 0x00e6, + 0x0174: 0x3300, + 0x017a: 0x3000, + 0x017e: 0x3300, // Block 0x6, offset 0x180 - 0x0180: 0x3000, 0x0183: 0x1100, 0x0184: 0x1100, 0x0185: 0x1100, - 0x0186: 0x1100, 0x0187: 0x1100, 0x0188: 0x1100, 0x0189: 0x3000, - 0x018c: 0x9900, 0x018d: 0x9900, 0x018e: 0x1100, 0x018f: 0x1100, 0x0190: 0x1100, 0x0191: 0x1100, - 0x0194: 0x1100, 0x0195: 0x1100, 0x0196: 0x1100, 0x0197: 0x1100, - 0x0198: 0x1100, 0x0199: 0x1100, 0x019a: 0x9900, 0x019b: 0x9900, 0x019c: 0x1100, 0x019d: 0x1100, - 0x019e: 0x1100, 0x019f: 0x1100, 0x01a0: 0x9900, 0x01a1: 0x9900, 0x01a2: 0x1100, 0x01a3: 0x1100, - 0x01a4: 0x1100, 0x01a5: 0x1100, 0x01a8: 0x9900, 0x01a9: 0x9900, - 0x01aa: 0x9900, 0x01ab: 0x9900, 0x01ac: 0x1100, 0x01ad: 0x1100, 0x01ae: 0x1100, 0x01af: 0x1100, - 0x01b0: 0x1100, 0x01b1: 0x1100, 0x01b2: 0x1100, 0x01b3: 0x1100, 0x01b4: 0x1100, 0x01b5: 0x1100, - 0x01b6: 0x1100, 0x01b7: 0x1100, 0x01b8: 0x1100, 0x01b9: 0x1100, 0x01ba: 0x1100, 0x01bb: 0x1100, - 0x01bc: 0x1100, 0x01bd: 0x1100, 0x01be: 0x1100, 0x01bf: 0x3800, + 0x0184: 0x3000, 0x0185: 0x3100, + 0x0186: 0x1100, 0x0187: 0x3300, 0x0188: 0x1100, 0x0189: 0x1100, 0x018a: 0x1100, + 0x018c: 0x1100, 0x018e: 0x1100, 0x018f: 0x1100, 0x0190: 0x1100, 0x0191: 0x8800, + 0x0195: 0x8800, 0x0197: 0x8800, + 0x0199: 0x8800, + 0x019f: 0x8800, 0x01a1: 0x8800, + 0x01a5: 0x8800, 0x01a9: 0x8800, + 0x01aa: 0x1100, 0x01ab: 0x1100, 0x01ac: 0x9900, 0x01ad: 0x1100, 0x01ae: 0x9900, 0x01af: 0x1100, + 0x01b0: 0x1100, 0x01b1: 0x8800, 0x01b5: 0x8800, + 0x01b7: 0x8800, 0x01b9: 0x8800, + 0x01bf: 0x8800, // Block 0x7, offset 0x1c0 - 0x01e0: 0x9900, 0x01e1: 0x9900, - 0x01ef: 0x9900, - 0x01f0: 0x9900, - 0x01f7: 0x8800, + 0x01c0: 0x1100, 0x01c1: 0x1100, 0x01c3: 0x1100, + 0x01c6: 0x8800, 0x01c7: 0x1100, + 0x01cc: 0x1100, 0x01cd: 0x1100, 0x01ce: 0x1100, 0x01d0: 0x8800, + 0x01d3: 0x8800, 0x01d5: 0x8800, 0x01d6: 0x8800, 0x01d7: 0x8800, + 0x01d8: 0x8800, 0x01d9: 0x1100, 0x01da: 0x8800, + 0x01de: 0x8800, 0x01e3: 0x8800, + 0x01e7: 0x8800, + 0x01eb: 0x8800, 0x01ed: 0x8800, + 0x01f0: 0x8800, 0x01f3: 0x8800, 0x01f5: 0x8800, + 0x01f6: 0x8800, 0x01f7: 0x8800, 0x01f8: 0x8800, 0x01f9: 0x1100, 0x01fa: 0x8800, + 0x01fe: 0x8800, // Block 0x8, offset 0x200 - 0x0204: 0x3000, 0x0205: 0x3000, - 0x0206: 0x3000, 0x0207: 0x3000, 0x0208: 0x3000, 0x0209: 0x3000, 0x020a: 0x3000, 0x020b: 0x3000, - 0x020c: 0x3000, 0x020d: 0x1100, 0x020e: 0x1100, 0x020f: 0x1100, 0x0210: 0x1100, 0x0211: 0x1100, - 0x0212: 0x1100, 0x0213: 0x1100, 0x0214: 0x1100, 0x0215: 0x1100, 0x0216: 0x1100, 0x0217: 0x1100, - 0x0218: 0x1100, 0x0219: 0x1100, 0x021a: 0x1100, 0x021b: 0x1100, 0x021c: 0x1100, - 0x021e: 0x1100, 0x021f: 0x1100, 0x0220: 0x1100, 0x0221: 0x1100, 0x0222: 0x1100, 0x0223: 0x1100, - 0x0226: 0x1100, 0x0227: 0x1100, 0x0228: 0x1100, 0x0229: 0x1100, - 0x022a: 0x9900, 0x022b: 0x9900, 0x022c: 0x1100, 0x022d: 0x1100, 0x022e: 0x1100, 0x022f: 0x1100, - 0x0230: 0x1100, 0x0231: 0x3000, 0x0232: 0x3000, 0x0233: 0x3000, 0x0234: 0x1100, 0x0235: 0x1100, - 0x0238: 0x1100, 0x0239: 0x1100, 0x023a: 0x1100, 0x023b: 0x1100, - 0x023c: 0x1100, 0x023d: 0x1100, 0x023e: 0x1100, 0x023f: 0x1100, + 0x0207: 0x3000, + 0x0211: 0x00dc, + 0x0212: 0x00e6, 0x0213: 0x00e6, 0x0214: 0x00e6, 0x0215: 0x00e6, 0x0216: 0x00dc, 0x0217: 0x00e6, + 0x0218: 0x00e6, 0x0219: 0x00e6, 0x021a: 0x00de, 0x021b: 0x00dc, 0x021c: 0x00e6, 0x021d: 0x00e6, + 0x021e: 0x00e6, 0x021f: 0x00e6, 0x0220: 0x00e6, 0x0221: 0x00e6, 0x0222: 0x00dc, 0x0223: 0x00dc, + 0x0224: 0x00dc, 0x0225: 0x00dc, 0x0226: 0x00dc, 0x0227: 0x00dc, 0x0228: 0x00e6, 0x0229: 0x00e6, + 0x022a: 0x00dc, 0x022b: 0x00e6, 0x022c: 0x00e6, 0x022d: 0x00de, 0x022e: 0x00e4, 0x022f: 0x00e6, + 0x0230: 0x000a, 0x0231: 0x000b, 0x0232: 0x000c, 0x0233: 0x000d, 0x0234: 0x000e, 0x0235: 0x000f, + 0x0236: 0x0010, 0x0237: 0x0011, 0x0238: 0x0012, 0x0239: 0x0013, 0x023a: 0x0013, 0x023b: 0x0014, + 0x023c: 0x0015, 0x023d: 0x0016, 0x023f: 0x0017, // Block 0x9, offset 0x240 - 0x0240: 0x1100, 0x0241: 0x1100, 0x0242: 0x1100, 0x0243: 0x1100, 0x0244: 0x1100, 0x0245: 0x1100, - 0x0246: 0x1100, 0x0247: 0x1100, 0x0248: 0x1100, 0x0249: 0x1100, 0x024a: 0x1100, 0x024b: 0x1100, - 0x024c: 0x1100, 0x024d: 0x1100, 0x024e: 0x1100, 0x024f: 0x1100, 0x0250: 0x1100, 0x0251: 0x1100, - 0x0252: 0x1100, 0x0253: 0x1100, 0x0254: 0x1100, 0x0255: 0x1100, 0x0256: 0x1100, 0x0257: 0x1100, - 0x0258: 0x1100, 0x0259: 0x1100, 0x025a: 0x1100, 0x025b: 0x1100, - 0x025e: 0x1100, 0x025f: 0x1100, - 0x0266: 0x9900, 0x0267: 0x9900, 0x0268: 0x9900, 0x0269: 0x9900, - 0x026a: 0x1100, 0x026b: 0x1100, 0x026c: 0x1100, 0x026d: 0x1100, 0x026e: 0x9900, 0x026f: 0x9900, - 0x0270: 0x1100, 0x0271: 0x1100, 0x0272: 0x1100, 0x0273: 0x1100, + 0x0248: 0x8800, 0x024a: 0x8800, 0x024b: 0x001b, + 0x024c: 0x001c, 0x024d: 0x001d, 0x024e: 0x001e, 0x024f: 0x001f, 0x0250: 0x0020, 0x0251: 0x0021, + 0x0252: 0x0022, 0x0253: 0x66e6, 0x0254: 0x66e6, 0x0255: 0x66dc, 0x0256: 0x00dc, 0x0257: 0x00e6, + 0x0258: 0x00e6, 0x0259: 0x00e6, 0x025a: 0x00e6, 0x025b: 0x00e6, 0x025c: 0x00dc, 0x025d: 0x00e6, + 0x025e: 0x00e6, 0x025f: 0x00dc, + 0x0270: 0x0023, 0x0275: 0x3000, + 0x0276: 0x3000, 0x0277: 0x3000, 0x0278: 0x3000, // Block 0xa, offset 0x280 - 0x0292: 0x8800, - 0x02b0: 0x3000, 0x02b1: 0x3000, 0x02b2: 0x3000, 0x02b3: 0x3000, 0x02b4: 0x3000, 0x02b5: 0x3000, - 0x02b6: 0x3000, 0x02b7: 0x3000, 0x02b8: 0x3000, + 0x0280: 0x9900, 0x0281: 0x9900, 0x0282: 0x1100, 0x0283: 0x1100, 0x0284: 0x1100, 0x0285: 0x1100, + 0x0288: 0x9900, 0x0289: 0x9900, 0x028a: 0x1100, 0x028b: 0x1100, + 0x028c: 0x1100, 0x028d: 0x1100, 0x0290: 0x9900, 0x0291: 0x9900, + 0x0292: 0x1100, 0x0293: 0x1100, 0x0294: 0x1100, 0x0295: 0x1100, 0x0296: 0x1100, 0x0297: 0x1100, + 0x0299: 0x9900, 0x029b: 0x1100, 0x029d: 0x1100, + 0x029f: 0x1100, 0x02a0: 0x9900, 0x02a1: 0x9900, 0x02a2: 0x9900, 0x02a3: 0x9900, + 0x02a4: 0x9900, 0x02a5: 0x9900, 0x02a6: 0x9900, 0x02a7: 0x9900, 0x02a8: 0x9900, 0x02a9: 0x9900, + 0x02aa: 0x9900, 0x02ab: 0x9900, 0x02ac: 0x9900, 0x02ad: 0x9900, 0x02ae: 0x9900, 0x02af: 0x9900, + 0x02b0: 0x9900, 0x02b1: 0x3300, 0x02b2: 0x1100, 0x02b3: 0x3300, 0x02b4: 0x9900, 0x02b5: 0x3300, + 0x02b6: 0x1100, 0x02b7: 0x3300, 0x02b8: 0x1100, 0x02b9: 0x3300, 0x02ba: 0x1100, 0x02bb: 0x3300, + 0x02bc: 0x9900, 0x02bd: 0x3300, // Block 0xb, offset 0x2c0 - 0x02d8: 0x3000, 0x02d9: 0x3000, 0x02da: 0x3000, 0x02db: 0x3000, 0x02dc: 0x3000, 0x02dd: 0x3000, - 0x02e0: 0x3000, 0x02e1: 0x3000, 0x02e2: 0x3000, 0x02e3: 0x3000, - 0x02e4: 0x3000, + 0x02c0: 0x3000, 0x02c1: 0x3100, 0x02c2: 0x1100, 0x02c3: 0x1100, 0x02c4: 0x1100, + 0x02c6: 0x9900, 0x02c7: 0x1100, 0x02c8: 0x1100, 0x02c9: 0x3300, 0x02ca: 0x1100, 0x02cb: 0x3300, + 0x02cc: 0x1100, 0x02cd: 0x3100, 0x02ce: 0x3100, 0x02cf: 0x3100, 0x02d0: 0x1100, 0x02d1: 0x1100, + 0x02d2: 0x1100, 0x02d3: 0x3300, 0x02d6: 0x1100, 0x02d7: 0x1100, + 0x02d8: 0x1100, 0x02d9: 0x1100, 0x02da: 0x1100, 0x02db: 0x3300, 0x02dd: 0x3100, + 0x02de: 0x3100, 0x02df: 0x3100, 0x02e0: 0x1100, 0x02e1: 0x1100, 0x02e2: 0x1100, 0x02e3: 0x3300, + 0x02e4: 0x1100, 0x02e5: 0x1100, 0x02e6: 0x1100, 0x02e7: 0x1100, 0x02e8: 0x1100, 0x02e9: 0x1100, + 0x02ea: 0x1100, 0x02eb: 0x3300, 0x02ec: 0x1100, 0x02ed: 0x3100, 0x02ee: 0x3300, 0x02ef: 0x3300, + 0x02f2: 0x1100, 0x02f3: 0x1100, 0x02f4: 0x1100, + 0x02f6: 0x9900, 0x02f7: 0x1100, 0x02f8: 0x1100, 0x02f9: 0x3300, 0x02fa: 0x1100, 0x02fb: 0x3300, + 0x02fc: 0x1100, 0x02fd: 0x3300, 0x02fe: 0x3800, // Block 0xc, offset 0x300 - 0x0300: 0x66e6, 0x0301: 0x66e6, 0x0302: 0x66e6, 0x0303: 0x66e6, 0x0304: 0x66e6, 0x0305: 0x00e6, - 0x0306: 0x66e6, 0x0307: 0x66e6, 0x0308: 0x66e6, 0x0309: 0x66e6, 0x030a: 0x66e6, 0x030b: 0x66e6, - 0x030c: 0x66e6, 0x030d: 0x00e6, 0x030e: 0x00e6, 0x030f: 0x66e6, 0x0310: 0x00e6, 0x0311: 0x66e6, - 0x0312: 0x00e6, 0x0313: 0x66e6, 0x0314: 0x66e6, 0x0315: 0x00e8, 0x0316: 0x00dc, 0x0317: 0x00dc, - 0x0318: 0x00dc, 0x0319: 0x00dc, 0x031a: 0x00e8, 0x031b: 0x66d8, 0x031c: 0x00dc, 0x031d: 0x00dc, - 0x031e: 0x00dc, 0x031f: 0x00dc, 0x0320: 0x00dc, 0x0321: 0x00ca, 0x0322: 0x00ca, 0x0323: 0x66dc, - 0x0324: 0x66dc, 0x0325: 0x66dc, 0x0326: 0x66dc, 0x0327: 0x66ca, 0x0328: 0x66ca, 0x0329: 0x00dc, - 0x032a: 0x00dc, 0x032b: 0x00dc, 0x032c: 0x00dc, 0x032d: 0x66dc, 0x032e: 0x66dc, 0x032f: 0x00dc, - 0x0330: 0x66dc, 0x0331: 0x66dc, 0x0332: 0x00dc, 0x0333: 0x00dc, 0x0334: 0x0001, 0x0335: 0x0001, - 0x0336: 0x0001, 0x0337: 0x0001, 0x0338: 0x6601, 0x0339: 0x00dc, 0x033a: 0x00dc, 0x033b: 0x00dc, - 0x033c: 0x00dc, 0x033d: 0x00e6, 0x033e: 0x00e6, 0x033f: 0x00e6, + 0x0301: 0x1100, 0x0303: 0x8800, 0x0304: 0x1100, 0x0305: 0x8800, + 0x0307: 0x1100, 0x0308: 0x8800, 0x0309: 0x1100, + 0x030d: 0x8800, + 0x0320: 0x1100, 0x0321: 0x8800, 0x0322: 0x1100, + 0x0324: 0x8800, 0x0325: 0x8800, + 0x032d: 0x1100, 0x032e: 0x1100, 0x032f: 0x1100, + 0x0330: 0x1100, 0x0331: 0x1100, 0x0332: 0x8800, 0x0333: 0x8800, 0x0334: 0x1100, 0x0335: 0x1100, + 0x0336: 0x8800, 0x0337: 0x8800, 0x0338: 0x1100, 0x0339: 0x1100, 0x033a: 0x8800, 0x033b: 0x8800, + 0x033c: 0x8800, 0x033d: 0x8800, // Block 0xd, offset 0x340 - 0x0340: 0x33e6, 0x0341: 0x33e6, 0x0342: 0x66e6, 0x0343: 0x33e6, 0x0344: 0x33e6, 0x0345: 0x66f0, - 0x0346: 0x00e6, 0x0347: 0x00dc, 0x0348: 0x00dc, 0x0349: 0x00dc, 0x034a: 0x00e6, 0x034b: 0x00e6, - 0x034c: 0x00e6, 0x034d: 0x00dc, 0x034e: 0x00dc, 0x0350: 0x00e6, 0x0351: 0x00e6, - 0x0352: 0x00e6, 0x0353: 0x00dc, 0x0354: 0x00dc, 0x0355: 0x00dc, 0x0356: 0x00dc, 0x0357: 0x00e6, - 0x0358: 0x00e8, 0x0359: 0x00dc, 0x035a: 0x00dc, 0x035b: 0x00e6, 0x035c: 0x00e9, 0x035d: 0x00ea, - 0x035e: 0x00ea, 0x035f: 0x00e9, 0x0360: 0x00ea, 0x0361: 0x00ea, 0x0362: 0x00e9, 0x0363: 0x00e6, - 0x0364: 0x00e6, 0x0365: 0x00e6, 0x0366: 0x00e6, 0x0367: 0x00e6, 0x0368: 0x00e6, 0x0369: 0x00e6, - 0x036a: 0x00e6, 0x036b: 0x00e6, 0x036c: 0x00e6, 0x036d: 0x00e6, 0x036e: 0x00e6, 0x036f: 0x00e6, - 0x0374: 0x3300, - 0x037a: 0x3000, - 0x037e: 0x3300, + 0x0346: 0x8800, 0x034b: 0x8800, + 0x034c: 0x1100, 0x034d: 0x8800, 0x034e: 0x1100, 0x034f: 0x8800, 0x0350: 0x1100, 0x0351: 0x8800, + 0x0352: 0x1100, 0x0353: 0x8800, 0x0354: 0x1100, 0x0355: 0x8800, 0x0356: 0x1100, 0x0357: 0x8800, + 0x0358: 0x1100, 0x0359: 0x8800, 0x035a: 0x1100, 0x035b: 0x8800, 0x035c: 0x1100, 0x035d: 0x8800, + 0x035e: 0x1100, 0x035f: 0x8800, 0x0360: 0x1100, 0x0361: 0x8800, 0x0362: 0x1100, + 0x0364: 0x8800, 0x0365: 0x1100, 0x0366: 0x8800, 0x0367: 0x1100, 0x0368: 0x8800, 0x0369: 0x1100, + 0x036f: 0x8800, + 0x0370: 0x1100, 0x0371: 0x1100, 0x0372: 0x8800, 0x0373: 0x1100, 0x0374: 0x1100, 0x0375: 0x8800, + 0x0376: 0x1100, 0x0377: 0x1100, 0x0378: 0x8800, 0x0379: 0x1100, 0x037a: 0x1100, 0x037b: 0x8800, + 0x037c: 0x1100, 0x037d: 0x1100, // Block 0xe, offset 0x380 - 0x0384: 0x3000, 0x0385: 0x3100, - 0x0386: 0x1100, 0x0387: 0x3300, 0x0388: 0x1100, 0x0389: 0x1100, 0x038a: 0x1100, - 0x038c: 0x1100, 0x038e: 0x1100, 0x038f: 0x1100, 0x0390: 0x1100, 0x0391: 0x8800, - 0x0395: 0x8800, 0x0397: 0x8800, - 0x0399: 0x8800, - 0x039f: 0x8800, 0x03a1: 0x8800, - 0x03a5: 0x8800, 0x03a9: 0x8800, - 0x03aa: 0x1100, 0x03ab: 0x1100, 0x03ac: 0x9900, 0x03ad: 0x1100, 0x03ae: 0x9900, 0x03af: 0x1100, - 0x03b0: 0x1100, 0x03b1: 0x8800, 0x03b5: 0x8800, - 0x03b7: 0x8800, 0x03b9: 0x8800, - 0x03bf: 0x8800, + 0x0394: 0x1100, + 0x0399: 0x6608, 0x039a: 0x6608, 0x039b: 0x3000, 0x039c: 0x3000, 0x039d: 0x8800, + 0x039e: 0x1100, 0x039f: 0x3000, + 0x03a6: 0x8800, + 0x03ab: 0x8800, 0x03ac: 0x1100, 0x03ad: 0x8800, 0x03ae: 0x1100, 0x03af: 0x8800, + 0x03b0: 0x1100, 0x03b1: 0x8800, 0x03b2: 0x1100, 0x03b3: 0x8800, 0x03b4: 0x1100, 0x03b5: 0x8800, + 0x03b6: 0x1100, 0x03b7: 0x8800, 0x03b8: 0x1100, 0x03b9: 0x8800, 0x03ba: 0x1100, 0x03bb: 0x8800, + 0x03bc: 0x1100, 0x03bd: 0x8800, 0x03be: 0x1100, 0x03bf: 0x8800, // Block 0xf, offset 0x3c0 - 0x03c1: 0x8800, 0x03c5: 0x8800, - 0x03c9: 0x8800, 0x03ca: 0x9900, 0x03cb: 0x9900, - 0x03cc: 0x1100, 0x03cd: 0x1100, 0x03ce: 0x9900, 0x03d0: 0x3000, 0x03d1: 0x3000, - 0x03d2: 0x3800, 0x03d3: 0x3100, 0x03d4: 0x3100, 0x03d5: 0x3000, 0x03d6: 0x3000, - 0x03f0: 0x3000, 0x03f1: 0x3000, 0x03f2: 0x3000, 0x03f4: 0x3000, 0x03f5: 0x3000, - 0x03f9: 0x3000, - // Block 0x10, offset 0x400 - 0x0400: 0x1100, 0x0401: 0x1100, 0x0403: 0x1100, - 0x0406: 0x8800, 0x0407: 0x1100, - 0x040c: 0x1100, 0x040d: 0x1100, 0x040e: 0x1100, 0x0410: 0x8800, - 0x0413: 0x8800, 0x0415: 0x8800, 0x0416: 0x8800, 0x0417: 0x8800, - 0x0418: 0x8800, 0x0419: 0x1100, 0x041a: 0x8800, - 0x041e: 0x8800, 0x0423: 0x8800, - 0x0427: 0x8800, - 0x042b: 0x8800, 0x042d: 0x8800, - 0x0430: 0x8800, 0x0433: 0x8800, 0x0435: 0x8800, - 0x0436: 0x8800, 0x0437: 0x8800, 0x0438: 0x8800, 0x0439: 0x1100, 0x043a: 0x8800, - 0x043e: 0x8800, - // Block 0x11, offset 0x440 - 0x0443: 0x8800, - 0x0447: 0x8800, 0x044b: 0x8800, - 0x044d: 0x8800, 0x0450: 0x1100, 0x0451: 0x1100, - 0x0453: 0x1100, 0x0456: 0x8800, 0x0457: 0x1100, - 0x045c: 0x1100, 0x045d: 0x1100, - 0x045e: 0x1100, - 0x0474: 0x8800, 0x0475: 0x8800, - 0x0476: 0x1100, 0x0477: 0x1100, - // Block 0x12, offset 0x480 - 0x0483: 0x00e6, 0x0484: 0x00e6, 0x0485: 0x00e6, - 0x0486: 0x00e6, 0x0487: 0x00e6, - // Block 0x13, offset 0x4c0 - 0x04c1: 0x1100, 0x04c2: 0x1100, - 0x04d0: 0x1100, 0x04d1: 0x1100, - 0x04d2: 0x1100, 0x04d3: 0x1100, 0x04d6: 0x1100, 0x04d7: 0x1100, - 0x04d8: 0x8800, 0x04d9: 0x8800, 0x04da: 0x1100, 0x04db: 0x1100, 0x04dc: 0x1100, 0x04dd: 0x1100, - 0x04de: 0x1100, 0x04df: 0x1100, 0x04e2: 0x1100, 0x04e3: 0x1100, - 0x04e4: 0x1100, 0x04e5: 0x1100, 0x04e6: 0x1100, 0x04e7: 0x1100, 0x04e8: 0x8800, 0x04e9: 0x8800, - 0x04ea: 0x1100, 0x04eb: 0x1100, 0x04ec: 0x1100, 0x04ed: 0x1100, 0x04ee: 0x1100, 0x04ef: 0x1100, - 0x04f0: 0x1100, 0x04f1: 0x1100, 0x04f2: 0x1100, 0x04f3: 0x1100, 0x04f4: 0x1100, 0x04f5: 0x1100, - 0x04f8: 0x1100, 0x04f9: 0x1100, - // Block 0x14, offset 0x500 - 0x0507: 0x3000, - 0x0511: 0x00dc, - 0x0512: 0x00e6, 0x0513: 0x00e6, 0x0514: 0x00e6, 0x0515: 0x00e6, 0x0516: 0x00dc, 0x0517: 0x00e6, - 0x0518: 0x00e6, 0x0519: 0x00e6, 0x051a: 0x00de, 0x051b: 0x00dc, 0x051c: 0x00e6, 0x051d: 0x00e6, - 0x051e: 0x00e6, 0x051f: 0x00e6, 0x0520: 0x00e6, 0x0521: 0x00e6, 0x0522: 0x00dc, 0x0523: 0x00dc, - 0x0524: 0x00dc, 0x0525: 0x00dc, 0x0526: 0x00dc, 0x0527: 0x00dc, 0x0528: 0x00e6, 0x0529: 0x00e6, - 0x052a: 0x00dc, 0x052b: 0x00e6, 0x052c: 0x00e6, 0x052d: 0x00de, 0x052e: 0x00e4, 0x052f: 0x00e6, - 0x0530: 0x000a, 0x0531: 0x000b, 0x0532: 0x000c, 0x0533: 0x000d, 0x0534: 0x000e, 0x0535: 0x000f, - 0x0536: 0x0010, 0x0537: 0x0011, 0x0538: 0x0012, 0x0539: 0x0013, 0x053a: 0x0013, 0x053b: 0x0014, - 0x053c: 0x0015, 0x053d: 0x0016, 0x053f: 0x0017, - // Block 0x15, offset 0x540 - 0x0541: 0x0018, 0x0542: 0x0019, 0x0544: 0x00e6, 0x0545: 0x00dc, - 0x0547: 0x0012, - // Block 0x16, offset 0x580 - 0x0590: 0x00e6, 0x0591: 0x00e6, - 0x0592: 0x00e6, 0x0593: 0x00e6, 0x0594: 0x00e6, 0x0595: 0x00e6, 0x0596: 0x00e6, 0x0597: 0x00e6, - 0x0598: 0x001e, 0x0599: 0x001f, 0x059a: 0x0020, - 0x05a2: 0x1100, 0x05a3: 0x1100, - 0x05a4: 0x1100, 0x05a5: 0x1100, 0x05a6: 0x1100, 0x05a7: 0x8800, - // Block 0x17, offset 0x5c0 - 0x05c8: 0x8800, 0x05ca: 0x8800, 0x05cb: 0x001b, - 0x05cc: 0x001c, 0x05cd: 0x001d, 0x05ce: 0x001e, 0x05cf: 0x001f, 0x05d0: 0x0020, 0x05d1: 0x0021, - 0x05d2: 0x0022, 0x05d3: 0x66e6, 0x05d4: 0x66e6, 0x05d5: 0x66dc, 0x05d6: 0x00dc, 0x05d7: 0x00e6, - 0x05d8: 0x00e6, 0x05d9: 0x00e6, 0x05da: 0x00e6, 0x05db: 0x00e6, 0x05dc: 0x00dc, 0x05dd: 0x00e6, - 0x05de: 0x00e6, 0x05df: 0x00dc, - 0x05f0: 0x0023, 0x05f5: 0x3000, - 0x05f6: 0x3000, 0x05f7: 0x3000, 0x05f8: 0x3000, - // Block 0x18, offset 0x600 - 0x0600: 0x1100, 0x0601: 0x8800, 0x0602: 0x1100, - 0x0612: 0x8800, 0x0613: 0x1100, 0x0615: 0x8800, 0x0616: 0x00e6, 0x0617: 0x00e6, - 0x0618: 0x00e6, 0x0619: 0x00e6, 0x061a: 0x00e6, 0x061b: 0x00e6, 0x061c: 0x00e6, - 0x061f: 0x00e6, 0x0620: 0x00e6, 0x0621: 0x00e6, 0x0622: 0x00e6, 0x0623: 0x00dc, - 0x0624: 0x00e6, 0x0627: 0x00e6, 0x0628: 0x00e6, - 0x062a: 0x00dc, 0x062b: 0x00e6, 0x062c: 0x00e6, 0x062d: 0x00dc, - // Block 0x19, offset 0x640 - 0x0651: 0x0024, - 0x0670: 0x00e6, 0x0671: 0x00dc, 0x0672: 0x00e6, 0x0673: 0x00e6, 0x0674: 0x00dc, 0x0675: 0x00e6, - 0x0676: 0x00e6, 0x0677: 0x00dc, 0x0678: 0x00dc, 0x0679: 0x00dc, 0x067a: 0x00e6, 0x067b: 0x00dc, - 0x067c: 0x00dc, 0x067d: 0x00e6, 0x067e: 0x00dc, 0x067f: 0x00e6, - // Block 0x1a, offset 0x680 - 0x0680: 0x00e6, 0x0681: 0x00e6, 0x0682: 0x00dc, 0x0683: 0x00e6, 0x0684: 0x00dc, 0x0685: 0x00e6, - 0x0686: 0x00dc, 0x0687: 0x00e6, 0x0688: 0x00dc, 0x0689: 0x00e6, 0x068a: 0x00e6, - // Block 0x1b, offset 0x6c0 - 0x06eb: 0x00e6, 0x06ec: 0x00e6, 0x06ed: 0x00e6, 0x06ee: 0x00e6, 0x06ef: 0x00e6, - 0x06f0: 0x00e6, 0x06f1: 0x00e6, 0x06f2: 0x00dc, 0x06f3: 0x00e6, - // Block 0x1c, offset 0x700 - 0x0716: 0x00e6, 0x0717: 0x00e6, - 0x0718: 0x00e6, 0x0719: 0x00e6, 0x071b: 0x00e6, 0x071c: 0x00e6, 0x071d: 0x00e6, - 0x071e: 0x00e6, 0x071f: 0x00e6, 0x0720: 0x00e6, 0x0721: 0x00e6, 0x0722: 0x00e6, 0x0723: 0x00e6, - 0x0725: 0x00e6, 0x0726: 0x00e6, 0x0727: 0x00e6, 0x0729: 0x00e6, - 0x072a: 0x00e6, 0x072b: 0x00e6, 0x072c: 0x00e6, 0x072d: 0x00e6, - // Block 0x1d, offset 0x740 - 0x0759: 0x00dc, 0x075a: 0x00dc, 0x075b: 0x00dc, - // Block 0x1e, offset 0x780 - 0x07a8: 0x8800, 0x07a9: 0x1100, - 0x07b0: 0x8800, 0x07b1: 0x1100, 0x07b3: 0x8800, 0x07b4: 0x1100, - 0x07bc: 0x6607, - // Block 0x1f, offset 0x7c0 - 0x07cd: 0x0009, 0x07d1: 0x00e6, - 0x07d2: 0x00dc, 0x07d3: 0x00e6, 0x07d4: 0x00e6, - 0x07d8: 0x3300, 0x07d9: 0x3300, 0x07da: 0x3300, 0x07db: 0x3300, 0x07dc: 0x3300, 0x07dd: 0x3300, - 0x07de: 0x3300, 0x07df: 0x3300, - // Block 0x20, offset 0x800 - 0x083c: 0x0007, 0x083e: 0x6600, - // Block 0x21, offset 0x840 - 0x0847: 0x8800, 0x084b: 0x1100, - 0x084c: 0x1100, 0x084d: 0x0009, - 0x0857: 0x6600, - 0x085c: 0x3300, 0x085d: 0x3300, - 0x085f: 0x3300, - // Block 0x22, offset 0x880 - 0x08b3: 0x3300, - 0x08b6: 0x3300, - 0x08bc: 0x0007, - // Block 0x23, offset 0x8c0 - 0x08cd: 0x0009, - 0x08d9: 0x3300, 0x08da: 0x3300, 0x08db: 0x3300, - 0x08de: 0x3300, - // Block 0x24, offset 0x900 - 0x093c: 0x0007, - // Block 0x25, offset 0x940 - 0x094d: 0x0009, - // Block 0x26, offset 0x980 - 0x0987: 0x8800, 0x0988: 0x1100, 0x098b: 0x1100, - 0x098c: 0x1100, 0x098d: 0x0009, - 0x0996: 0x6600, 0x0997: 0x6600, - 0x099c: 0x3300, 0x099d: 0x3300, - // Block 0x27, offset 0x9c0 - 0x09d2: 0x8800, 0x09d4: 0x1100, - 0x09fe: 0x6600, - // Block 0x28, offset 0xa00 - 0x0a06: 0x8800, 0x0a07: 0x8800, 0x0a0a: 0x1100, 0x0a0b: 0x1100, - 0x0a0c: 0x1100, 0x0a0d: 0x0009, - 0x0a17: 0x6600, - // Block 0x29, offset 0xa40 - 0x0a46: 0x8800, 0x0a48: 0x1100, - 0x0a4d: 0x0009, - 0x0a55: 0x0054, 0x0a56: 0x665b, - // Block 0x2a, offset 0xa80 - 0x0abc: 0x0007, 0x0abf: 0x8800, - // Block 0x2b, offset 0xac0 - 0x0ac0: 0x1100, 0x0ac2: 0x6600, - 0x0ac6: 0x8800, 0x0ac7: 0x1100, 0x0ac8: 0x1100, 0x0aca: 0x9900, 0x0acb: 0x1100, - 0x0acd: 0x0009, - 0x0ad5: 0x6600, 0x0ad6: 0x6600, - // Block 0x2c, offset 0xb00 - 0x0b3e: 0x6600, - // Block 0x2d, offset 0xb40 - 0x0b4a: 0x6609, - 0x0b4f: 0x6600, - 0x0b59: 0x8800, 0x0b5a: 0x1100, 0x0b5c: 0x9900, 0x0b5d: 0x1100, - 0x0b5e: 0x1100, 0x0b5f: 0x6600, - // Block 0x2e, offset 0xb80 - 0x0bb3: 0x3000, - 0x0bb8: 0x0067, 0x0bb9: 0x0067, 0x0bba: 0x0009, - // Block 0x2f, offset 0xbc0 - 0x0bc8: 0x006b, 0x0bc9: 0x006b, 0x0bca: 0x006b, 0x0bcb: 0x006b, - // Block 0x30, offset 0xc00 - 0x0c33: 0x3000, - 0x0c38: 0x0076, 0x0c39: 0x0076, - // Block 0x31, offset 0xc40 - 0x0c48: 0x007a, 0x0c49: 0x007a, 0x0c4a: 0x007a, 0x0c4b: 0x007a, - 0x0c5c: 0x3000, 0x0c5d: 0x3000, - // Block 0x32, offset 0xc80 - 0x0c8c: 0x3000, - 0x0c98: 0x00dc, 0x0c99: 0x00dc, - 0x0cb5: 0x00dc, - 0x0cb7: 0x00dc, 0x0cb9: 0x00d8, - // Block 0x33, offset 0xcc0 - 0x0cc3: 0x3300, - 0x0ccd: 0x3300, - 0x0cd2: 0x3300, 0x0cd7: 0x3300, - 0x0cdc: 0x3300, - 0x0ce9: 0x3300, - 0x0cf1: 0x0081, 0x0cf2: 0x0082, 0x0cf3: 0x3300, 0x0cf4: 0x0084, 0x0cf5: 0x3300, - 0x0cf6: 0x3300, 0x0cf7: 0x3000, 0x0cf8: 0x3300, 0x0cf9: 0x3000, 0x0cfa: 0x0082, 0x0cfb: 0x0082, - 0x0cfc: 0x0082, 0x0cfd: 0x0082, - // Block 0x34, offset 0xd00 - 0x0d00: 0x0082, 0x0d01: 0x3300, 0x0d02: 0x00e6, 0x0d03: 0x00e6, 0x0d04: 0x0009, - 0x0d06: 0x00e6, 0x0d07: 0x00e6, - 0x0d13: 0x3300, - 0x0d1d: 0x3300, - 0x0d22: 0x3300, - 0x0d27: 0x3300, - 0x0d2c: 0x3300, - 0x0d39: 0x3300, - // Block 0x35, offset 0xd40 - 0x0d46: 0x00dc, - // Block 0x36, offset 0xd80 - 0x0da5: 0x8800, 0x0da6: 0x1100, - 0x0dae: 0x6600, - 0x0db7: 0x0007, 0x0db9: 0x0009, 0x0dba: 0x0009, - // Block 0x37, offset 0xdc0 - 0x0dcd: 0x00dc, - // Block 0x38, offset 0xe00 - 0x0e3c: 0x3000, - // Block 0x39, offset 0xe40 - 0x0e40: 0x8800, 0x0e41: 0x8800, 0x0e42: 0x8800, 0x0e43: 0x8800, 0x0e44: 0x8800, 0x0e45: 0x8800, - 0x0e46: 0x8800, 0x0e47: 0x8800, 0x0e48: 0x8800, 0x0e49: 0x8800, 0x0e4a: 0x8800, 0x0e4b: 0x8800, - 0x0e4c: 0x8800, 0x0e4d: 0x8800, 0x0e4e: 0x8800, 0x0e4f: 0x8800, 0x0e50: 0x8800, 0x0e51: 0x8800, - 0x0e52: 0x8800, - // Block 0x3a, offset 0xe80 - 0x0ea1: 0xee00, 0x0ea2: 0xee00, 0x0ea3: 0xee00, - 0x0ea4: 0xee00, 0x0ea5: 0xee00, 0x0ea6: 0xee00, 0x0ea7: 0xee00, 0x0ea8: 0xee00, 0x0ea9: 0xee00, - 0x0eaa: 0xee00, 0x0eab: 0xee00, 0x0eac: 0xee00, 0x0ead: 0xee00, 0x0eae: 0xee00, 0x0eaf: 0xee00, - 0x0eb0: 0xee00, 0x0eb1: 0xee00, 0x0eb2: 0xee00, 0x0eb3: 0xee00, 0x0eb4: 0xee00, 0x0eb5: 0xee00, - 0x0eb6: 0xee00, 0x0eb7: 0xee00, 0x0eb8: 0xee00, 0x0eb9: 0xee00, 0x0eba: 0xee00, 0x0ebb: 0xee00, - 0x0ebc: 0xee00, 0x0ebd: 0xee00, 0x0ebe: 0xee00, 0x0ebf: 0xee00, - // Block 0x3b, offset 0xec0 - 0x0ec0: 0xee00, 0x0ec1: 0xee00, 0x0ec2: 0xee00, 0x0ec3: 0xee00, 0x0ec4: 0xee00, 0x0ec5: 0xee00, - 0x0ec6: 0xee00, 0x0ec7: 0xee00, 0x0ec8: 0xee00, 0x0ec9: 0xee00, 0x0eca: 0xee00, 0x0ecb: 0xee00, - 0x0ecc: 0xee00, 0x0ecd: 0xee00, 0x0ece: 0xee00, 0x0ecf: 0xee00, 0x0ed0: 0xee00, 0x0ed1: 0xee00, - 0x0ed2: 0xee00, 0x0ed3: 0xee00, 0x0ed4: 0xee00, 0x0ed5: 0xee00, 0x0ed6: 0xee00, 0x0ed7: 0xee00, - 0x0ed8: 0xee00, 0x0ed9: 0xee00, 0x0eda: 0xee00, 0x0edb: 0xee00, 0x0edc: 0xee00, 0x0edd: 0xee00, - 0x0ede: 0xee00, 0x0edf: 0xee00, 0x0ee0: 0xee00, 0x0ee1: 0xee00, 0x0ee2: 0xee00, 0x0ee3: 0xee00, - 0x0ee4: 0xee00, 0x0ee5: 0xee00, 0x0ee6: 0xee00, 0x0ee7: 0xee00, 0x0ee8: 0xee00, 0x0ee9: 0xee00, - 0x0eea: 0xee00, 0x0eeb: 0xee00, 0x0eec: 0xee00, 0x0eed: 0xee00, 0x0eee: 0xee00, 0x0eef: 0xee00, - 0x0ef0: 0xee00, 0x0ef1: 0xee00, 0x0ef2: 0xee00, 0x0ef3: 0xee00, 0x0ef4: 0xee00, 0x0ef5: 0xee00, - 0x0ef6: 0xee00, 0x0ef7: 0xee00, 0x0ef8: 0xee00, 0x0ef9: 0xee00, 0x0efa: 0xee00, 0x0efb: 0xee00, - 0x0efc: 0xee00, 0x0efd: 0xee00, 0x0efe: 0xee00, 0x0eff: 0xee00, - // Block 0x3c, offset 0xf00 - 0x0f00: 0xee00, 0x0f01: 0xee00, 0x0f02: 0xee00, - // Block 0x3d, offset 0xf40 - 0x0f5d: 0x00e6, - 0x0f5e: 0x00e6, 0x0f5f: 0x00e6, - // Block 0x3e, offset 0xf80 - 0x0f94: 0x0009, - 0x0fb4: 0x0009, - // Block 0x3f, offset 0xfc0 - 0x0fd2: 0x0009, - 0x0fdd: 0x00e6, - // Block 0x40, offset 0x1000 - 0x1029: 0x00e4, - // Block 0x41, offset 0x1040 - 0x1079: 0x00de, 0x107a: 0x00e6, 0x107b: 0x00dc, - // Block 0x42, offset 0x1080 - 0x1097: 0x00e6, - 0x1098: 0x00dc, - // Block 0x43, offset 0x10c0 - 0x10e0: 0x0009, - 0x10f5: 0x00e6, - 0x10f6: 0x00e6, 0x10f7: 0x00e6, 0x10f8: 0x00e6, 0x10f9: 0x00e6, 0x10fa: 0x00e6, 0x10fb: 0x00e6, - 0x10fc: 0x00e6, 0x10ff: 0x00dc, - // Block 0x44, offset 0x1100 - 0x1105: 0x8800, - 0x1106: 0x1100, 0x1107: 0x8800, 0x1108: 0x1100, 0x1109: 0x8800, 0x110a: 0x1100, 0x110b: 0x8800, - 0x110c: 0x1100, 0x110d: 0x8800, 0x110e: 0x1100, 0x1111: 0x8800, - 0x1112: 0x1100, - 0x1134: 0x0007, 0x1135: 0x6600, - 0x113a: 0x8800, 0x113b: 0x1100, - 0x113c: 0x8800, 0x113d: 0x1100, 0x113e: 0x8800, 0x113f: 0x8800, - // Block 0x45, offset 0x1140 - 0x1140: 0x1100, 0x1141: 0x1100, 0x1142: 0x8800, 0x1143: 0x1100, 0x1144: 0x0009, - 0x116b: 0x00e6, 0x116c: 0x00dc, 0x116d: 0x00e6, 0x116e: 0x00e6, 0x116f: 0x00e6, - 0x1170: 0x00e6, 0x1171: 0x00e6, 0x1172: 0x00e6, 0x1173: 0x00e6, - // Block 0x46, offset 0x1180 - 0x11aa: 0x0009, - // Block 0x47, offset 0x11c0 - 0x11e6: 0x0007, - 0x11f2: 0x0009, 0x11f3: 0x0009, - // Block 0x48, offset 0x1200 - 0x1237: 0x0007, - // Block 0x49, offset 0x1240 - 0x1250: 0x00e6, 0x1251: 0x00e6, - 0x1252: 0x00e6, 0x1254: 0x0001, 0x1255: 0x00dc, 0x1256: 0x00dc, 0x1257: 0x00dc, - 0x1258: 0x00dc, 0x1259: 0x00dc, 0x125a: 0x00e6, 0x125b: 0x00e6, 0x125c: 0x00dc, 0x125d: 0x00dc, - 0x125e: 0x00dc, 0x125f: 0x00dc, 0x1260: 0x00e6, 0x1262: 0x0001, 0x1263: 0x0001, - 0x1264: 0x0001, 0x1265: 0x0001, 0x1266: 0x0001, 0x1267: 0x0001, 0x1268: 0x0001, - 0x126d: 0x00dc, - // Block 0x4a, offset 0x1280 - 0x12ac: 0x3000, 0x12ad: 0x3000, 0x12ae: 0x3000, - 0x12b0: 0x3000, 0x12b1: 0x3000, 0x12b2: 0x3000, 0x12b3: 0x3000, 0x12b4: 0x3000, 0x12b5: 0x3000, - 0x12b6: 0x3000, 0x12b7: 0x3000, 0x12b8: 0x3000, 0x12b9: 0x3000, 0x12ba: 0x3000, - 0x12bc: 0x3000, 0x12bd: 0x3000, 0x12be: 0x3000, 0x12bf: 0x3000, - // Block 0x4b, offset 0x12c0 - 0x12c0: 0x3000, 0x12c1: 0x3000, 0x12c2: 0x3000, 0x12c3: 0x3000, 0x12c4: 0x3000, 0x12c5: 0x3000, - 0x12c6: 0x3000, 0x12c7: 0x3000, 0x12c8: 0x3000, 0x12c9: 0x3000, 0x12ca: 0x3000, 0x12cb: 0x3000, - 0x12cc: 0x3000, 0x12cd: 0x3000, 0x12cf: 0x3000, 0x12d0: 0x3000, 0x12d1: 0x3000, - 0x12d2: 0x3000, 0x12d3: 0x3000, 0x12d4: 0x3000, 0x12d5: 0x3000, 0x12d6: 0x3000, 0x12d7: 0x3000, - 0x12d8: 0x3000, 0x12d9: 0x3000, 0x12da: 0x3000, 0x12db: 0x3000, 0x12dc: 0x3000, 0x12dd: 0x3000, - 0x12de: 0x3000, 0x12df: 0x3000, 0x12e0: 0x3000, 0x12e1: 0x3000, 0x12e2: 0x3000, 0x12e3: 0x3000, - 0x12e4: 0x3000, 0x12e5: 0x3000, 0x12e6: 0x3000, 0x12e7: 0x3000, 0x12e8: 0x3000, 0x12e9: 0x3000, - 0x12ea: 0x3000, - 0x12f8: 0x3000, - // Block 0x4c, offset 0x1300 - 0x131b: 0x3000, 0x131c: 0x3000, 0x131d: 0x3000, - 0x131e: 0x3000, 0x131f: 0x3000, 0x1320: 0x3000, 0x1321: 0x3000, 0x1322: 0x3000, 0x1323: 0x3000, - 0x1324: 0x3000, 0x1325: 0x3000, 0x1326: 0x3000, 0x1327: 0x3000, 0x1328: 0x3000, 0x1329: 0x3000, - 0x132a: 0x3000, 0x132b: 0x3000, 0x132c: 0x3000, 0x132d: 0x3000, 0x132e: 0x3000, 0x132f: 0x3000, - 0x1330: 0x3000, 0x1331: 0x3000, 0x1332: 0x3000, 0x1333: 0x3000, 0x1334: 0x3000, 0x1335: 0x3000, - 0x1336: 0x3000, 0x1337: 0x3000, 0x1338: 0x3000, 0x1339: 0x3000, 0x133a: 0x3000, 0x133b: 0x3000, - 0x133c: 0x3000, 0x133d: 0x3000, 0x133e: 0x3000, 0x133f: 0x3000, - // Block 0x4d, offset 0x1340 - 0x1340: 0x00e6, 0x1341: 0x00e6, 0x1342: 0x00dc, 0x1343: 0x00e6, 0x1344: 0x00e6, 0x1345: 0x00e6, - 0x1346: 0x00e6, 0x1347: 0x00e6, 0x1348: 0x00e6, 0x1349: 0x00e6, 0x134a: 0x00dc, 0x134b: 0x00e6, - 0x134c: 0x00e6, 0x134d: 0x00ea, 0x134e: 0x00d6, 0x134f: 0x00dc, 0x1350: 0x00ca, 0x1351: 0x00e6, - 0x1352: 0x00e6, 0x1353: 0x00e6, 0x1354: 0x00e6, 0x1355: 0x00e6, 0x1356: 0x00e6, 0x1357: 0x00e6, - 0x1358: 0x00e6, 0x1359: 0x00e6, 0x135a: 0x00e6, 0x135b: 0x00e6, 0x135c: 0x00e6, 0x135d: 0x00e6, - 0x135e: 0x00e6, 0x135f: 0x00e6, 0x1360: 0x00e6, 0x1361: 0x00e6, 0x1362: 0x00e6, 0x1363: 0x00e6, - 0x1364: 0x00e6, 0x1365: 0x00e6, 0x1366: 0x00e6, - 0x137c: 0x00e9, 0x137d: 0x00dc, 0x137e: 0x00e6, 0x137f: 0x00dc, - // Block 0x4e, offset 0x1380 - 0x1380: 0x1100, 0x1381: 0x1100, 0x1382: 0x1100, 0x1383: 0x1100, 0x1384: 0x1100, 0x1385: 0x1100, - 0x1386: 0x1100, 0x1387: 0x1100, 0x1388: 0x1100, 0x1389: 0x1100, 0x138a: 0x1100, 0x138b: 0x1100, - 0x138c: 0x1100, 0x138d: 0x1100, 0x138e: 0x1100, 0x138f: 0x1100, 0x1390: 0x1100, 0x1391: 0x1100, - 0x1392: 0x1100, 0x1393: 0x1100, 0x1394: 0x1100, 0x1395: 0x1100, 0x1396: 0x1100, 0x1397: 0x1100, - 0x1398: 0x1100, 0x1399: 0x1100, 0x139a: 0x1100, 0x139b: 0x1100, 0x139c: 0x1100, 0x139d: 0x1100, - 0x139e: 0x1100, 0x139f: 0x1100, 0x13a0: 0x1100, 0x13a1: 0x1100, 0x13a2: 0x1100, 0x13a3: 0x1100, - 0x13a4: 0x1100, 0x13a5: 0x1100, 0x13a6: 0x1100, 0x13a7: 0x1100, 0x13a8: 0x1100, 0x13a9: 0x1100, - 0x13aa: 0x1100, 0x13ab: 0x1100, 0x13ac: 0x1100, 0x13ad: 0x1100, 0x13ae: 0x1100, 0x13af: 0x1100, - 0x13b0: 0x1100, 0x13b1: 0x1100, 0x13b2: 0x1100, 0x13b3: 0x1100, 0x13b4: 0x1100, 0x13b5: 0x1100, - 0x13b6: 0x9900, 0x13b7: 0x9900, 0x13b8: 0x1100, 0x13b9: 0x1100, 0x13ba: 0x1100, 0x13bb: 0x1100, - 0x13bc: 0x1100, 0x13bd: 0x1100, 0x13be: 0x1100, 0x13bf: 0x1100, - // Block 0x4f, offset 0x13c0 - 0x13c0: 0x1100, 0x13c1: 0x1100, 0x13c2: 0x1100, 0x13c3: 0x1100, 0x13c4: 0x1100, 0x13c5: 0x1100, - 0x13c6: 0x1100, 0x13c7: 0x1100, 0x13c8: 0x1100, 0x13c9: 0x1100, 0x13ca: 0x1100, 0x13cb: 0x1100, - 0x13cc: 0x1100, 0x13cd: 0x1100, 0x13ce: 0x1100, 0x13cf: 0x1100, 0x13d0: 0x1100, 0x13d1: 0x1100, - 0x13d2: 0x1100, 0x13d3: 0x1100, 0x13d4: 0x1100, 0x13d5: 0x1100, 0x13d6: 0x1100, 0x13d7: 0x1100, - 0x13d8: 0x1100, 0x13d9: 0x1100, 0x13da: 0x9900, 0x13db: 0x9900, 0x13dc: 0x1100, 0x13dd: 0x1100, - 0x13de: 0x1100, 0x13df: 0x1100, 0x13e0: 0x1100, 0x13e1: 0x1100, 0x13e2: 0x9900, 0x13e3: 0x9900, - 0x13e4: 0x1100, 0x13e5: 0x1100, 0x13e6: 0x1100, 0x13e7: 0x1100, 0x13e8: 0x1100, 0x13e9: 0x1100, - 0x13ea: 0x1100, 0x13eb: 0x1100, 0x13ec: 0x1100, 0x13ed: 0x1100, 0x13ee: 0x1100, 0x13ef: 0x1100, - 0x13f0: 0x1100, 0x13f1: 0x1100, 0x13f2: 0x1100, 0x13f3: 0x1100, 0x13f4: 0x1100, 0x13f5: 0x1100, - 0x13f6: 0x1100, 0x13f7: 0x1100, 0x13f8: 0x1100, 0x13f9: 0x1100, 0x13fa: 0x1100, 0x13fb: 0x1100, - 0x13fc: 0x1100, 0x13fd: 0x1100, 0x13fe: 0x1100, 0x13ff: 0x1100, - // Block 0x50, offset 0x1400 - 0x1400: 0x1100, 0x1401: 0x1100, 0x1402: 0x1100, 0x1403: 0x1100, 0x1404: 0x1100, 0x1405: 0x1100, - 0x1406: 0x1100, 0x1407: 0x1100, 0x1408: 0x1100, 0x1409: 0x1100, 0x140a: 0x1100, 0x140b: 0x1100, - 0x140c: 0x1100, 0x140d: 0x1100, 0x140e: 0x1100, 0x140f: 0x1100, 0x1410: 0x1100, 0x1411: 0x1100, - 0x1412: 0x1100, 0x1413: 0x1100, 0x1414: 0x1100, 0x1415: 0x1100, 0x1416: 0x1100, 0x1417: 0x1100, - 0x1418: 0x1100, 0x1419: 0x1100, 0x141a: 0x3000, 0x141b: 0x3100, - 0x1420: 0x9900, 0x1421: 0x9900, 0x1422: 0x1100, 0x1423: 0x1100, - 0x1424: 0x1100, 0x1425: 0x1100, 0x1426: 0x1100, 0x1427: 0x1100, 0x1428: 0x1100, 0x1429: 0x1100, - 0x142a: 0x1100, 0x142b: 0x1100, 0x142c: 0x1100, 0x142d: 0x1100, 0x142e: 0x1100, 0x142f: 0x1100, - 0x1430: 0x1100, 0x1431: 0x1100, 0x1432: 0x1100, 0x1433: 0x1100, 0x1434: 0x1100, 0x1435: 0x1100, - 0x1436: 0x1100, 0x1437: 0x1100, 0x1438: 0x9900, 0x1439: 0x9900, 0x143a: 0x1100, 0x143b: 0x1100, - 0x143c: 0x1100, 0x143d: 0x1100, 0x143e: 0x1100, 0x143f: 0x1100, - // Block 0x51, offset 0x1440 - 0x1440: 0x1100, 0x1441: 0x1100, 0x1442: 0x1100, 0x1443: 0x1100, 0x1444: 0x1100, 0x1445: 0x1100, - 0x1446: 0x1100, 0x1447: 0x1100, 0x1448: 0x1100, 0x1449: 0x1100, 0x144a: 0x1100, 0x144b: 0x1100, - 0x144c: 0x9900, 0x144d: 0x9900, 0x144e: 0x1100, 0x144f: 0x1100, 0x1450: 0x1100, 0x1451: 0x1100, - 0x1452: 0x1100, 0x1453: 0x1100, 0x1454: 0x1100, 0x1455: 0x1100, 0x1456: 0x1100, 0x1457: 0x1100, - 0x1458: 0x1100, 0x1459: 0x1100, 0x145a: 0x1100, 0x145b: 0x1100, 0x145c: 0x1100, 0x145d: 0x1100, - 0x145e: 0x1100, 0x145f: 0x1100, 0x1460: 0x1100, 0x1461: 0x1100, 0x1462: 0x1100, 0x1463: 0x1100, - 0x1464: 0x1100, 0x1465: 0x1100, 0x1466: 0x1100, 0x1467: 0x1100, 0x1468: 0x1100, 0x1469: 0x1100, - 0x146a: 0x1100, 0x146b: 0x1100, 0x146c: 0x1100, 0x146d: 0x1100, 0x146e: 0x1100, 0x146f: 0x1100, - 0x1470: 0x1100, 0x1471: 0x1100, 0x1472: 0x1100, 0x1473: 0x1100, 0x1474: 0x1100, 0x1475: 0x1100, - 0x1476: 0x1100, 0x1477: 0x1100, 0x1478: 0x1100, 0x1479: 0x1100, - // Block 0x52, offset 0x1480 - 0x1480: 0x9900, 0x1481: 0x9900, 0x1482: 0x9900, 0x1483: 0x9900, 0x1484: 0x9900, 0x1485: 0x9900, - 0x1486: 0x9900, 0x1487: 0x9900, 0x1488: 0x9900, 0x1489: 0x9900, 0x148a: 0x9900, 0x148b: 0x9900, - 0x148c: 0x9900, 0x148d: 0x9900, 0x148e: 0x9900, 0x148f: 0x9900, 0x1490: 0x9900, 0x1491: 0x9900, - 0x1492: 0x1100, 0x1493: 0x1100, 0x1494: 0x1100, 0x1495: 0x1100, - 0x1498: 0x9900, 0x1499: 0x9900, 0x149a: 0x1100, 0x149b: 0x1100, 0x149c: 0x1100, 0x149d: 0x1100, - 0x14a0: 0x9900, 0x14a1: 0x9900, 0x14a2: 0x9900, 0x14a3: 0x9900, - 0x14a4: 0x9900, 0x14a5: 0x9900, 0x14a6: 0x9900, 0x14a7: 0x9900, 0x14a8: 0x9900, 0x14a9: 0x9900, - 0x14aa: 0x9900, 0x14ab: 0x9900, 0x14ac: 0x9900, 0x14ad: 0x9900, 0x14ae: 0x9900, 0x14af: 0x9900, - 0x14b0: 0x9900, 0x14b1: 0x9900, 0x14b2: 0x1100, 0x14b3: 0x1100, 0x14b4: 0x1100, 0x14b5: 0x1100, - 0x14b6: 0x1100, 0x14b7: 0x1100, 0x14b8: 0x9900, 0x14b9: 0x9900, 0x14ba: 0x1100, 0x14bb: 0x1100, - 0x14bc: 0x1100, 0x14bd: 0x1100, 0x14be: 0x1100, 0x14bf: 0x1100, - // Block 0x53, offset 0x14c0 - 0x14c0: 0x9900, 0x14c1: 0x9900, 0x14c2: 0x1100, 0x14c3: 0x1100, 0x14c4: 0x1100, 0x14c5: 0x1100, - 0x14c8: 0x9900, 0x14c9: 0x9900, 0x14ca: 0x1100, 0x14cb: 0x1100, - 0x14cc: 0x1100, 0x14cd: 0x1100, 0x14d0: 0x9900, 0x14d1: 0x9900, - 0x14d2: 0x1100, 0x14d3: 0x1100, 0x14d4: 0x1100, 0x14d5: 0x1100, 0x14d6: 0x1100, 0x14d7: 0x1100, - 0x14d9: 0x9900, 0x14db: 0x1100, 0x14dd: 0x1100, - 0x14df: 0x1100, 0x14e0: 0x9900, 0x14e1: 0x9900, 0x14e2: 0x9900, 0x14e3: 0x9900, - 0x14e4: 0x9900, 0x14e5: 0x9900, 0x14e6: 0x9900, 0x14e7: 0x9900, 0x14e8: 0x9900, 0x14e9: 0x9900, - 0x14ea: 0x9900, 0x14eb: 0x9900, 0x14ec: 0x9900, 0x14ed: 0x9900, 0x14ee: 0x9900, 0x14ef: 0x9900, - 0x14f0: 0x9900, 0x14f1: 0x3300, 0x14f2: 0x1100, 0x14f3: 0x3300, 0x14f4: 0x9900, 0x14f5: 0x3300, - 0x14f6: 0x1100, 0x14f7: 0x3300, 0x14f8: 0x1100, 0x14f9: 0x3300, 0x14fa: 0x1100, 0x14fb: 0x3300, - 0x14fc: 0x9900, 0x14fd: 0x3300, - // Block 0x54, offset 0x1500 - 0x1500: 0x1100, 0x1501: 0x1100, 0x1502: 0x1100, 0x1503: 0x1100, 0x1504: 0x1100, 0x1505: 0x1100, - 0x1506: 0x1100, 0x1507: 0x1100, 0x1508: 0x1100, 0x1509: 0x1100, 0x150a: 0x1100, 0x150b: 0x1100, - 0x150c: 0x1100, 0x150d: 0x1100, 0x150e: 0x1100, 0x150f: 0x1100, 0x1510: 0x1100, 0x1511: 0x1100, - 0x1512: 0x1100, 0x1513: 0x1100, 0x1514: 0x1100, 0x1515: 0x1100, 0x1516: 0x1100, 0x1517: 0x1100, - 0x1518: 0x1100, 0x1519: 0x1100, 0x151a: 0x1100, 0x151b: 0x1100, 0x151c: 0x1100, 0x151d: 0x1100, - 0x151e: 0x1100, 0x151f: 0x1100, 0x1520: 0x1100, 0x1521: 0x1100, 0x1522: 0x1100, 0x1523: 0x1100, - 0x1524: 0x1100, 0x1525: 0x1100, 0x1526: 0x1100, 0x1527: 0x1100, 0x1528: 0x1100, 0x1529: 0x1100, - 0x152a: 0x1100, 0x152b: 0x1100, 0x152c: 0x1100, 0x152d: 0x1100, 0x152e: 0x1100, 0x152f: 0x1100, - 0x1530: 0x1100, 0x1531: 0x1100, 0x1532: 0x1100, 0x1533: 0x1100, 0x1534: 0x1100, - 0x1536: 0x9900, 0x1537: 0x1100, 0x1538: 0x1100, 0x1539: 0x1100, 0x153a: 0x1100, 0x153b: 0x3300, - 0x153c: 0x1100, 0x153d: 0x3000, 0x153e: 0x3300, 0x153f: 0x3800, - // Block 0x55, offset 0x1540 - 0x1540: 0x3000, 0x1541: 0x3100, 0x1542: 0x1100, 0x1543: 0x1100, 0x1544: 0x1100, - 0x1546: 0x9900, 0x1547: 0x1100, 0x1548: 0x1100, 0x1549: 0x3300, 0x154a: 0x1100, 0x154b: 0x3300, - 0x154c: 0x1100, 0x154d: 0x3100, 0x154e: 0x3100, 0x154f: 0x3100, 0x1550: 0x1100, 0x1551: 0x1100, - 0x1552: 0x1100, 0x1553: 0x3300, 0x1556: 0x1100, 0x1557: 0x1100, - 0x1558: 0x1100, 0x1559: 0x1100, 0x155a: 0x1100, 0x155b: 0x3300, 0x155d: 0x3100, - 0x155e: 0x3100, 0x155f: 0x3100, 0x1560: 0x1100, 0x1561: 0x1100, 0x1562: 0x1100, 0x1563: 0x3300, - 0x1564: 0x1100, 0x1565: 0x1100, 0x1566: 0x1100, 0x1567: 0x1100, 0x1568: 0x1100, 0x1569: 0x1100, - 0x156a: 0x1100, 0x156b: 0x3300, 0x156c: 0x1100, 0x156d: 0x3100, 0x156e: 0x3300, 0x156f: 0x3300, - 0x1572: 0x1100, 0x1573: 0x1100, 0x1574: 0x1100, - 0x1576: 0x9900, 0x1577: 0x1100, 0x1578: 0x1100, 0x1579: 0x3300, 0x157a: 0x1100, 0x157b: 0x3300, - 0x157c: 0x1100, 0x157d: 0x3300, 0x157e: 0x3800, - // Block 0x56, offset 0x1580 - 0x1580: 0x3300, 0x1581: 0x3300, 0x1582: 0x3000, 0x1583: 0x3000, 0x1584: 0x3000, 0x1585: 0x3000, - 0x1586: 0x3000, 0x1587: 0x3000, 0x1588: 0x3000, 0x1589: 0x3000, 0x158a: 0x3000, - 0x1591: 0x3000, - 0x1597: 0x3000, - 0x15a4: 0x3000, 0x15a5: 0x3000, 0x15a6: 0x3000, - 0x15af: 0x3000, - 0x15b3: 0x3000, 0x15b4: 0x3000, - 0x15b6: 0x3000, 0x15b7: 0x3000, - 0x15bc: 0x3000, 0x15be: 0x3000, - // Block 0x57, offset 0x15c0 - 0x15c7: 0x3000, 0x15c8: 0x3000, 0x15c9: 0x3000, - 0x15d7: 0x3000, - 0x15df: 0x3000, - 0x15f0: 0x3000, 0x15f1: 0x3000, 0x15f4: 0x3000, 0x15f5: 0x3000, - 0x15f6: 0x3000, 0x15f7: 0x3000, 0x15f8: 0x3000, 0x15f9: 0x3000, 0x15fa: 0x3000, 0x15fb: 0x3000, - 0x15fc: 0x3000, 0x15fd: 0x3000, 0x15fe: 0x3000, 0x15ff: 0x3000, - // Block 0x58, offset 0x1600 - 0x1600: 0x3000, 0x1601: 0x3000, 0x1602: 0x3000, 0x1603: 0x3000, 0x1604: 0x3000, 0x1605: 0x3000, - 0x1606: 0x3000, 0x1607: 0x3000, 0x1608: 0x3000, 0x1609: 0x3000, 0x160a: 0x3000, 0x160b: 0x3000, - 0x160c: 0x3000, 0x160d: 0x3000, 0x160e: 0x3000, 0x1610: 0x3000, 0x1611: 0x3000, - 0x1612: 0x3000, 0x1613: 0x3000, 0x1614: 0x3000, 0x1615: 0x3000, 0x1616: 0x3000, 0x1617: 0x3000, - 0x1618: 0x3000, 0x1619: 0x3000, 0x161a: 0x3000, 0x161b: 0x3000, 0x161c: 0x3000, - 0x1628: 0x3000, - // Block 0x59, offset 0x1640 - 0x1650: 0x00e6, 0x1651: 0x00e6, - 0x1652: 0x0001, 0x1653: 0x0001, 0x1654: 0x00e6, 0x1655: 0x00e6, 0x1656: 0x00e6, 0x1657: 0x00e6, - 0x1658: 0x0001, 0x1659: 0x0001, 0x165a: 0x0001, 0x165b: 0x00e6, 0x165c: 0x00e6, - 0x1661: 0x00e6, - 0x1665: 0x0001, 0x1666: 0x0001, 0x1667: 0x00e6, 0x1668: 0x00dc, 0x1669: 0x00e6, - 0x166a: 0x0001, 0x166b: 0x0001, 0x166c: 0x00dc, 0x166d: 0x00dc, 0x166e: 0x00dc, 0x166f: 0x00dc, - 0x1670: 0x00e6, - // Block 0x5a, offset 0x1680 - 0x1680: 0x3000, 0x1681: 0x3000, 0x1682: 0x3000, 0x1683: 0x3000, 0x1685: 0x3000, - 0x1686: 0x3000, 0x1687: 0x3000, 0x1689: 0x3000, 0x168a: 0x3000, 0x168b: 0x3000, - 0x168c: 0x3000, 0x168d: 0x3000, 0x168e: 0x3000, 0x168f: 0x3000, 0x1690: 0x3000, 0x1691: 0x3000, - 0x1692: 0x3000, 0x1693: 0x3000, 0x1695: 0x3000, 0x1696: 0x3000, - 0x1699: 0x3000, 0x169a: 0x3000, 0x169b: 0x3000, 0x169c: 0x3000, 0x169d: 0x3000, - 0x16a0: 0x3000, 0x16a1: 0x3000, 0x16a2: 0x3000, - 0x16a4: 0x3000, 0x16a6: 0x3300, 0x16a8: 0x3000, - 0x16aa: 0x3300, 0x16ab: 0x3300, 0x16ac: 0x3000, 0x16ad: 0x3000, 0x16af: 0x3000, - 0x16b0: 0x3000, 0x16b1: 0x3000, 0x16b3: 0x3000, 0x16b4: 0x3000, 0x16b5: 0x3000, - 0x16b6: 0x3000, 0x16b7: 0x3000, 0x16b8: 0x3000, 0x16b9: 0x3000, 0x16bb: 0x3000, - 0x16bc: 0x3000, 0x16bd: 0x3000, 0x16be: 0x3000, 0x16bf: 0x3000, - // Block 0x5b, offset 0x16c0 - 0x16c0: 0x3000, 0x16c5: 0x3000, - 0x16c6: 0x3000, 0x16c7: 0x3000, 0x16c8: 0x3000, 0x16c9: 0x3000, - 0x16d0: 0x3000, 0x16d1: 0x3000, - 0x16d2: 0x3000, 0x16d3: 0x3000, 0x16d4: 0x3000, 0x16d5: 0x3000, 0x16d6: 0x3000, 0x16d7: 0x3000, - 0x16d8: 0x3000, 0x16d9: 0x3000, 0x16da: 0x3000, 0x16db: 0x3000, 0x16dc: 0x3000, 0x16dd: 0x3000, - 0x16de: 0x3000, 0x16df: 0x3000, 0x16e0: 0x3000, 0x16e1: 0x3000, 0x16e2: 0x3000, 0x16e3: 0x3000, - 0x16e4: 0x3000, 0x16e5: 0x3000, 0x16e6: 0x3000, 0x16e7: 0x3000, 0x16e8: 0x3000, 0x16e9: 0x3000, - 0x16ea: 0x3000, 0x16eb: 0x3000, 0x16ec: 0x3000, 0x16ed: 0x3000, 0x16ee: 0x3000, 0x16ef: 0x3000, - 0x16f0: 0x3000, 0x16f1: 0x3000, 0x16f2: 0x3000, 0x16f3: 0x3000, 0x16f4: 0x3000, 0x16f5: 0x3000, - 0x16f6: 0x3000, 0x16f7: 0x3000, 0x16f8: 0x3000, 0x16f9: 0x3000, 0x16fa: 0x3000, 0x16fb: 0x3000, - 0x16fc: 0x3000, 0x16fd: 0x3000, 0x16fe: 0x3000, 0x16ff: 0x3000, - // Block 0x5c, offset 0x1700 - 0x1709: 0x3000, - 0x1710: 0x8800, - 0x1712: 0x8800, 0x1714: 0x8800, - 0x171a: 0x1100, 0x171b: 0x1100, - 0x172e: 0x1100, - // Block 0x5d, offset 0x1740 - 0x174d: 0x1100, 0x174e: 0x1100, 0x174f: 0x1100, 0x1750: 0x8800, - 0x1752: 0x8800, 0x1754: 0x8800, - // Block 0x5e, offset 0x1780 - 0x1783: 0x8800, 0x1784: 0x1100, - 0x1788: 0x8800, 0x1789: 0x1100, 0x178b: 0x8800, - 0x178c: 0x1100, - 0x17a3: 0x8800, - 0x17a4: 0x1100, 0x17a5: 0x8800, 0x17a6: 0x1100, - 0x17ac: 0x3000, 0x17ad: 0x3000, 0x17af: 0x3000, - 0x17b0: 0x3000, - 0x17bc: 0x8800, - // Block 0x5f, offset 0x17c0 - 0x17c1: 0x1100, 0x17c3: 0x8800, 0x17c4: 0x1100, 0x17c5: 0x8800, - 0x17c7: 0x1100, 0x17c8: 0x8800, 0x17c9: 0x1100, - 0x17cd: 0x8800, - 0x17e0: 0x1100, 0x17e1: 0x8800, 0x17e2: 0x1100, - 0x17e4: 0x8800, 0x17e5: 0x8800, - 0x17ed: 0x1100, 0x17ee: 0x1100, 0x17ef: 0x1100, - 0x17f0: 0x1100, 0x17f1: 0x1100, 0x17f2: 0x8800, 0x17f3: 0x8800, 0x17f4: 0x1100, 0x17f5: 0x1100, - 0x17f6: 0x8800, 0x17f7: 0x8800, 0x17f8: 0x1100, 0x17f9: 0x1100, 0x17fa: 0x8800, 0x17fb: 0x8800, - 0x17fc: 0x8800, 0x17fd: 0x8800, - // Block 0x60, offset 0x1800 - 0x1800: 0x1100, 0x1801: 0x1100, 0x1802: 0x8800, 0x1803: 0x8800, 0x1804: 0x1100, 0x1805: 0x1100, - 0x1806: 0x8800, 0x1807: 0x8800, 0x1808: 0x1100, 0x1809: 0x1100, - 0x1811: 0x8800, - 0x1812: 0x8800, - 0x1822: 0x8800, - 0x1828: 0x8800, 0x1829: 0x8800, - 0x182b: 0x8800, 0x182c: 0x1100, 0x182d: 0x1100, 0x182e: 0x1100, 0x182f: 0x1100, - 0x1832: 0x8800, 0x1833: 0x8800, 0x1834: 0x8800, 0x1835: 0x8800, - // Block 0x61, offset 0x1840 - 0x1860: 0x1100, 0x1861: 0x1100, 0x1862: 0x1100, 0x1863: 0x1100, - 0x186a: 0x1100, 0x186b: 0x1100, 0x186c: 0x1100, 0x186d: 0x1100, - // Block 0x62, offset 0x1880 - 0x18a9: 0x3300, - 0x18aa: 0x3300, - // Block 0x63, offset 0x18c0 - 0x18e0: 0x3000, 0x18e1: 0x3000, 0x18e2: 0x3000, 0x18e3: 0x3000, - 0x18e4: 0x3000, 0x18e5: 0x3000, 0x18e6: 0x3000, 0x18e7: 0x3000, 0x18e8: 0x3000, 0x18e9: 0x3000, - 0x18ea: 0x3000, 0x18eb: 0x3000, 0x18ec: 0x3000, 0x18ed: 0x3000, 0x18ee: 0x3000, 0x18ef: 0x3000, - 0x18f0: 0x3000, 0x18f1: 0x3000, 0x18f2: 0x3000, 0x18f3: 0x3000, 0x18f4: 0x3000, 0x18f5: 0x3000, - 0x18f6: 0x3000, 0x18f7: 0x3000, 0x18f8: 0x3000, 0x18f9: 0x3000, 0x18fa: 0x3000, 0x18fb: 0x3000, - 0x18fc: 0x3000, 0x18fd: 0x3000, 0x18fe: 0x3000, 0x18ff: 0x3000, - // Block 0x64, offset 0x1900 - 0x1900: 0x3000, 0x1901: 0x3000, 0x1902: 0x3000, 0x1903: 0x3000, 0x1904: 0x3000, 0x1905: 0x3000, - 0x1906: 0x3000, 0x1907: 0x3000, 0x1908: 0x3000, 0x1909: 0x3000, 0x190a: 0x3000, 0x190b: 0x3000, - 0x190c: 0x3000, 0x190d: 0x3000, 0x190e: 0x3000, 0x190f: 0x3000, 0x1910: 0x3000, 0x1911: 0x3000, - 0x1912: 0x3000, 0x1913: 0x3000, 0x1914: 0x3000, 0x1915: 0x3000, 0x1916: 0x3000, 0x1917: 0x3000, - 0x1918: 0x3000, 0x1919: 0x3000, 0x191a: 0x3000, 0x191b: 0x3000, 0x191c: 0x3000, 0x191d: 0x3000, - 0x191e: 0x3000, 0x191f: 0x3000, 0x1920: 0x3000, 0x1921: 0x3000, 0x1922: 0x3000, 0x1923: 0x3000, - 0x1924: 0x3000, 0x1925: 0x3000, 0x1926: 0x3000, 0x1927: 0x3000, 0x1928: 0x3000, 0x1929: 0x3000, - 0x192a: 0x3000, 0x192b: 0x3000, 0x192c: 0x3000, 0x192d: 0x3000, 0x192e: 0x3000, 0x192f: 0x3000, - 0x1930: 0x3000, 0x1931: 0x3000, 0x1932: 0x3000, 0x1933: 0x3000, 0x1934: 0x3000, 0x1935: 0x3000, - 0x1936: 0x3000, 0x1937: 0x3000, 0x1938: 0x3000, 0x1939: 0x3000, 0x193a: 0x3000, 0x193b: 0x3000, - 0x193c: 0x3000, 0x193d: 0x3000, 0x193e: 0x3000, 0x193f: 0x3000, - // Block 0x65, offset 0x1940 - 0x1940: 0x3000, 0x1941: 0x3000, 0x1942: 0x3000, 0x1943: 0x3000, 0x1944: 0x3000, 0x1945: 0x3000, - 0x1946: 0x3000, 0x1947: 0x3000, 0x1948: 0x3000, 0x1949: 0x3000, 0x194a: 0x3000, 0x194b: 0x3000, - 0x194c: 0x3000, 0x194d: 0x3000, 0x194e: 0x3000, 0x194f: 0x3000, 0x1950: 0x3000, 0x1951: 0x3000, - 0x1952: 0x3000, 0x1953: 0x3000, 0x1954: 0x3000, 0x1955: 0x3000, 0x1956: 0x3000, 0x1957: 0x3000, - 0x1958: 0x3000, 0x1959: 0x3000, 0x195a: 0x3000, 0x195b: 0x3000, 0x195c: 0x3000, 0x195d: 0x3000, - 0x195e: 0x3000, 0x195f: 0x3000, 0x1960: 0x3000, 0x1961: 0x3000, 0x1962: 0x3000, 0x1963: 0x3000, - 0x1964: 0x3000, 0x1965: 0x3000, 0x1966: 0x3000, 0x1967: 0x3000, 0x1968: 0x3000, 0x1969: 0x3000, - 0x196a: 0x3000, - // Block 0x66, offset 0x1980 - 0x198c: 0x3000, - // Block 0x67, offset 0x19c0 - 0x19f4: 0x3000, 0x19f5: 0x3000, - 0x19f6: 0x3000, - // Block 0x68, offset 0x1a00 - 0x1a1c: 0x3300, - // Block 0x69, offset 0x1a40 - 0x1a7c: 0x3000, 0x1a7d: 0x3000, - // Block 0x6a, offset 0x1a80 - 0x1aaf: 0x00e6, - 0x1ab0: 0x00e6, 0x1ab1: 0x00e6, - // Block 0x6b, offset 0x1ac0 - 0x1aef: 0x3000, - 0x1aff: 0x0009, - // Block 0x6c, offset 0x1b00 - 0x1b20: 0x00e6, 0x1b21: 0x00e6, 0x1b22: 0x00e6, 0x1b23: 0x00e6, - 0x1b24: 0x00e6, 0x1b25: 0x00e6, 0x1b26: 0x00e6, 0x1b27: 0x00e6, 0x1b28: 0x00e6, 0x1b29: 0x00e6, - 0x1b2a: 0x00e6, 0x1b2b: 0x00e6, 0x1b2c: 0x00e6, 0x1b2d: 0x00e6, 0x1b2e: 0x00e6, 0x1b2f: 0x00e6, - 0x1b30: 0x00e6, 0x1b31: 0x00e6, 0x1b32: 0x00e6, 0x1b33: 0x00e6, 0x1b34: 0x00e6, 0x1b35: 0x00e6, - 0x1b36: 0x00e6, 0x1b37: 0x00e6, 0x1b38: 0x00e6, 0x1b39: 0x00e6, 0x1b3a: 0x00e6, 0x1b3b: 0x00e6, - 0x1b3c: 0x00e6, 0x1b3d: 0x00e6, 0x1b3e: 0x00e6, 0x1b3f: 0x00e6, - // Block 0x6d, offset 0x1b40 - 0x1b5f: 0x3000, - // Block 0x6e, offset 0x1b80 - 0x1bb3: 0x3000, - // Block 0x6f, offset 0x1bc0 - 0x1bc0: 0x3000, 0x1bc1: 0x3000, 0x1bc2: 0x3000, 0x1bc3: 0x3000, 0x1bc4: 0x3000, 0x1bc5: 0x3000, - 0x1bc6: 0x3000, 0x1bc7: 0x3000, 0x1bc8: 0x3000, 0x1bc9: 0x3000, 0x1bca: 0x3000, 0x1bcb: 0x3000, - 0x1bcc: 0x3000, 0x1bcd: 0x3000, 0x1bce: 0x3000, 0x1bcf: 0x3000, 0x1bd0: 0x3000, 0x1bd1: 0x3000, - 0x1bd2: 0x3000, 0x1bd3: 0x3000, 0x1bd4: 0x3000, 0x1bd5: 0x3000, - // Block 0x70, offset 0x1c00 - 0x1c00: 0x3000, - 0x1c2a: 0x00da, 0x1c2b: 0x00e4, 0x1c2c: 0x00e8, 0x1c2d: 0x00de, 0x1c2e: 0x00e0, 0x1c2f: 0x00e0, - 0x1c36: 0x3000, 0x1c38: 0x3000, 0x1c39: 0x3000, 0x1c3a: 0x3000, - // Block 0x71, offset 0x1c40 - 0x1c46: 0x8800, 0x1c4b: 0x8800, - 0x1c4c: 0x1100, 0x1c4d: 0x8800, 0x1c4e: 0x1100, 0x1c4f: 0x8800, 0x1c50: 0x1100, 0x1c51: 0x8800, - 0x1c52: 0x1100, 0x1c53: 0x8800, 0x1c54: 0x1100, 0x1c55: 0x8800, 0x1c56: 0x1100, 0x1c57: 0x8800, - 0x1c58: 0x1100, 0x1c59: 0x8800, 0x1c5a: 0x1100, 0x1c5b: 0x8800, 0x1c5c: 0x1100, 0x1c5d: 0x8800, - 0x1c5e: 0x1100, 0x1c5f: 0x8800, 0x1c60: 0x1100, 0x1c61: 0x8800, 0x1c62: 0x1100, - 0x1c64: 0x8800, 0x1c65: 0x1100, 0x1c66: 0x8800, 0x1c67: 0x1100, 0x1c68: 0x8800, 0x1c69: 0x1100, - 0x1c6f: 0x8800, - 0x1c70: 0x1100, 0x1c71: 0x1100, 0x1c72: 0x8800, 0x1c73: 0x1100, 0x1c74: 0x1100, 0x1c75: 0x8800, - 0x1c76: 0x1100, 0x1c77: 0x1100, 0x1c78: 0x8800, 0x1c79: 0x1100, 0x1c7a: 0x1100, 0x1c7b: 0x8800, - 0x1c7c: 0x1100, 0x1c7d: 0x1100, - // Block 0x72, offset 0x1c80 - 0x1c94: 0x1100, - 0x1c99: 0x6608, 0x1c9a: 0x6608, 0x1c9b: 0x3000, 0x1c9c: 0x3000, 0x1c9d: 0x8800, - 0x1c9e: 0x1100, 0x1c9f: 0x3000, - 0x1ca6: 0x8800, - 0x1cab: 0x8800, 0x1cac: 0x1100, 0x1cad: 0x8800, 0x1cae: 0x1100, 0x1caf: 0x8800, - 0x1cb0: 0x1100, 0x1cb1: 0x8800, 0x1cb2: 0x1100, 0x1cb3: 0x8800, 0x1cb4: 0x1100, 0x1cb5: 0x8800, - 0x1cb6: 0x1100, 0x1cb7: 0x8800, 0x1cb8: 0x1100, 0x1cb9: 0x8800, 0x1cba: 0x1100, 0x1cbb: 0x8800, - 0x1cbc: 0x1100, 0x1cbd: 0x8800, 0x1cbe: 0x1100, 0x1cbf: 0x8800, - // Block 0x73, offset 0x1cc0 - 0x1cc0: 0x1100, 0x1cc1: 0x8800, 0x1cc2: 0x1100, 0x1cc4: 0x8800, 0x1cc5: 0x1100, - 0x1cc6: 0x8800, 0x1cc7: 0x1100, 0x1cc8: 0x8800, 0x1cc9: 0x1100, - 0x1ccf: 0x8800, 0x1cd0: 0x1100, 0x1cd1: 0x1100, - 0x1cd2: 0x8800, 0x1cd3: 0x1100, 0x1cd4: 0x1100, 0x1cd5: 0x8800, 0x1cd6: 0x1100, 0x1cd7: 0x1100, - 0x1cd8: 0x8800, 0x1cd9: 0x1100, 0x1cda: 0x1100, 0x1cdb: 0x8800, 0x1cdc: 0x1100, 0x1cdd: 0x1100, - 0x1cef: 0x8800, - 0x1cf0: 0x8800, 0x1cf1: 0x8800, 0x1cf2: 0x8800, 0x1cf4: 0x1100, - 0x1cf7: 0x1100, 0x1cf8: 0x1100, 0x1cf9: 0x1100, 0x1cfa: 0x1100, - 0x1cfd: 0x8800, 0x1cfe: 0x1100, 0x1cff: 0x3000, - // Block 0x74, offset 0x1d00 - 0x1d31: 0x3000, 0x1d32: 0x3000, 0x1d33: 0x3000, 0x1d34: 0x3000, 0x1d35: 0x3000, - 0x1d36: 0x3000, 0x1d37: 0x3000, 0x1d38: 0x3000, 0x1d39: 0x3000, 0x1d3a: 0x3000, 0x1d3b: 0x3000, - 0x1d3c: 0x3000, 0x1d3d: 0x3000, 0x1d3e: 0x3000, 0x1d3f: 0x3000, - // Block 0x75, offset 0x1d40 - 0x1d40: 0x3000, 0x1d41: 0x3000, 0x1d42: 0x3000, 0x1d43: 0x3000, 0x1d44: 0x3000, 0x1d45: 0x3000, - 0x1d46: 0x3000, 0x1d47: 0x3000, 0x1d48: 0x3000, 0x1d49: 0x3000, 0x1d4a: 0x3000, 0x1d4b: 0x3000, - 0x1d4c: 0x3000, 0x1d4d: 0x3000, 0x1d4e: 0x3000, - 0x1d52: 0x3000, 0x1d53: 0x3000, 0x1d54: 0x3000, 0x1d55: 0x3000, 0x1d56: 0x3000, 0x1d57: 0x3000, - 0x1d58: 0x3000, 0x1d59: 0x3000, 0x1d5a: 0x3000, 0x1d5b: 0x3000, 0x1d5c: 0x3000, 0x1d5d: 0x3000, - 0x1d5e: 0x3000, 0x1d5f: 0x3000, - // Block 0x76, offset 0x1d80 - 0x1d80: 0x3000, 0x1d81: 0x3000, 0x1d82: 0x3000, 0x1d83: 0x3000, 0x1d84: 0x3000, 0x1d85: 0x3000, - 0x1d86: 0x3000, 0x1d87: 0x3000, 0x1d88: 0x3000, 0x1d89: 0x3000, 0x1d8a: 0x3000, 0x1d8b: 0x3000, - 0x1d8c: 0x3000, 0x1d8d: 0x3000, 0x1d8e: 0x3000, 0x1d8f: 0x3000, 0x1d90: 0x3000, 0x1d91: 0x3000, - 0x1d92: 0x3000, 0x1d93: 0x3000, 0x1d94: 0x3000, 0x1d95: 0x3000, 0x1d96: 0x3000, 0x1d97: 0x3000, - 0x1d98: 0x3000, 0x1d99: 0x3000, 0x1d9a: 0x3000, 0x1d9b: 0x3000, 0x1d9c: 0x3000, 0x1d9d: 0x3000, - 0x1d9e: 0x3000, 0x1da0: 0x3000, 0x1da1: 0x3000, 0x1da2: 0x3000, 0x1da3: 0x3000, - 0x1da4: 0x3000, 0x1da5: 0x3000, 0x1da6: 0x3000, 0x1da7: 0x3000, 0x1da8: 0x3000, 0x1da9: 0x3000, - 0x1daa: 0x3000, 0x1dab: 0x3000, 0x1dac: 0x3000, 0x1dad: 0x3000, 0x1dae: 0x3000, 0x1daf: 0x3000, - 0x1db0: 0x3000, 0x1db1: 0x3000, 0x1db2: 0x3000, 0x1db3: 0x3000, 0x1db4: 0x3000, 0x1db5: 0x3000, - 0x1db6: 0x3000, 0x1db7: 0x3000, 0x1db8: 0x3000, 0x1db9: 0x3000, 0x1dba: 0x3000, 0x1dbb: 0x3000, - 0x1dbc: 0x3000, 0x1dbd: 0x3000, 0x1dbe: 0x3000, 0x1dbf: 0x3000, - // Block 0x77, offset 0x1dc0 - 0x1dc0: 0x3000, 0x1dc1: 0x3000, 0x1dc2: 0x3000, 0x1dc3: 0x3000, 0x1dc4: 0x3000, 0x1dc5: 0x3000, - 0x1dc6: 0x3000, 0x1dc7: 0x3000, - 0x1dd0: 0x3000, 0x1dd1: 0x3000, - 0x1dd2: 0x3000, 0x1dd3: 0x3000, 0x1dd4: 0x3000, 0x1dd5: 0x3000, 0x1dd6: 0x3000, 0x1dd7: 0x3000, - 0x1dd8: 0x3000, 0x1dd9: 0x3000, 0x1dda: 0x3000, 0x1ddb: 0x3000, 0x1ddc: 0x3000, 0x1ddd: 0x3000, - 0x1dde: 0x3000, 0x1ddf: 0x3000, 0x1de0: 0x3000, 0x1de1: 0x3000, 0x1de2: 0x3000, 0x1de3: 0x3000, - 0x1de4: 0x3000, 0x1de5: 0x3000, 0x1de6: 0x3000, 0x1de7: 0x3000, 0x1de8: 0x3000, 0x1de9: 0x3000, - 0x1dea: 0x3000, 0x1deb: 0x3000, 0x1dec: 0x3000, 0x1ded: 0x3000, 0x1dee: 0x3000, 0x1def: 0x3000, - 0x1df0: 0x3000, 0x1df1: 0x3000, 0x1df2: 0x3000, 0x1df3: 0x3000, 0x1df4: 0x3000, 0x1df5: 0x3000, - 0x1df6: 0x3000, 0x1df7: 0x3000, 0x1df8: 0x3000, 0x1df9: 0x3000, 0x1dfa: 0x3000, 0x1dfb: 0x3000, - 0x1dfc: 0x3000, 0x1dfd: 0x3000, 0x1dfe: 0x3000, - // Block 0x78, offset 0x1e00 - 0x1e00: 0x3000, 0x1e01: 0x3000, 0x1e02: 0x3000, 0x1e03: 0x3000, 0x1e04: 0x3000, 0x1e05: 0x3000, - 0x1e06: 0x3000, 0x1e07: 0x3000, 0x1e08: 0x3000, 0x1e09: 0x3000, 0x1e0a: 0x3000, 0x1e0b: 0x3000, - 0x1e0c: 0x3000, 0x1e0d: 0x3000, 0x1e0e: 0x3000, 0x1e0f: 0x3000, 0x1e10: 0x3000, 0x1e11: 0x3000, - 0x1e12: 0x3000, 0x1e13: 0x3000, 0x1e14: 0x3000, 0x1e15: 0x3000, 0x1e16: 0x3000, 0x1e17: 0x3000, - 0x1e18: 0x3000, 0x1e19: 0x3000, 0x1e1a: 0x3000, 0x1e1b: 0x3000, 0x1e1c: 0x3000, 0x1e1d: 0x3000, - 0x1e1e: 0x3000, 0x1e1f: 0x3000, 0x1e20: 0x3000, 0x1e21: 0x3000, 0x1e22: 0x3000, 0x1e23: 0x3000, - 0x1e24: 0x3000, 0x1e25: 0x3000, 0x1e26: 0x3000, 0x1e27: 0x3000, 0x1e28: 0x3000, 0x1e29: 0x3000, - 0x1e2a: 0x3000, 0x1e2b: 0x3000, 0x1e2c: 0x3000, 0x1e2d: 0x3000, 0x1e2e: 0x3000, 0x1e2f: 0x3000, - 0x1e30: 0x3000, 0x1e31: 0x3000, 0x1e32: 0x3000, 0x1e33: 0x3000, 0x1e34: 0x3000, 0x1e35: 0x3000, - 0x1e36: 0x3000, 0x1e37: 0x3000, 0x1e38: 0x3000, 0x1e39: 0x3000, 0x1e3a: 0x3000, 0x1e3b: 0x3000, - 0x1e3c: 0x3000, 0x1e3d: 0x3000, 0x1e3e: 0x3000, - // Block 0x79, offset 0x1e40 - 0x1e6f: 0x00e6, - 0x1e7c: 0x00e6, 0x1e7d: 0x00e6, - // Block 0x7a, offset 0x1e80 - 0x1eb0: 0x00e6, 0x1eb1: 0x00e6, - // Block 0x7b, offset 0x1ec0 - 0x1ef0: 0x3000, - // Block 0x7c, offset 0x1f00 - 0x1f06: 0x0009, - // Block 0x7d, offset 0x1f40 - 0x1f44: 0x0009, - 0x1f60: 0x00e6, 0x1f61: 0x00e6, 0x1f62: 0x00e6, 0x1f63: 0x00e6, - 0x1f64: 0x00e6, 0x1f65: 0x00e6, 0x1f66: 0x00e6, 0x1f67: 0x00e6, 0x1f68: 0x00e6, 0x1f69: 0x00e6, - 0x1f6a: 0x00e6, 0x1f6b: 0x00e6, 0x1f6c: 0x00e6, 0x1f6d: 0x00e6, 0x1f6e: 0x00e6, 0x1f6f: 0x00e6, - 0x1f70: 0x00e6, 0x1f71: 0x00e6, - // Block 0x7e, offset 0x1f80 - 0x1fab: 0x00dc, 0x1fac: 0x00dc, 0x1fad: 0x00dc, - // Block 0x7f, offset 0x1fc0 - 0x1fd3: 0x0009, - // Block 0x80, offset 0x2000 - 0x2033: 0x0007, - // Block 0x81, offset 0x2040 - 0x2040: 0x0009, - // Block 0x82, offset 0x2080 - 0x20b0: 0x00e6, 0x20b2: 0x00e6, 0x20b3: 0x00e6, 0x20b4: 0x00dc, - 0x20b7: 0x00e6, 0x20b8: 0x00e6, - 0x20be: 0x00e6, 0x20bf: 0x00e6, - // Block 0x83, offset 0x20c0 - 0x20c1: 0x00e6, - // Block 0x84, offset 0x2100 - 0x212d: 0x0009, - // Block 0x85, offset 0x2140 - 0x2140: 0x1100, 0x2141: 0x1100, 0x2142: 0x1100, 0x2143: 0x1100, 0x2144: 0x1100, 0x2145: 0x1100, - 0x2146: 0x1100, 0x2147: 0x1100, 0x2148: 0x1100, 0x2149: 0x1100, 0x214a: 0x1100, 0x214b: 0x1100, - 0x214c: 0x1100, 0x214d: 0x1100, 0x214e: 0x1100, 0x214f: 0x1100, 0x2150: 0x1100, 0x2151: 0x1100, - 0x2152: 0x1100, 0x2153: 0x1100, 0x2154: 0x1100, 0x2155: 0x1100, 0x2156: 0x1100, 0x2157: 0x1100, - 0x2158: 0x1100, 0x2159: 0x1100, 0x215a: 0x1100, 0x215b: 0x1100, 0x215c: 0x1100, 0x215d: 0x1100, - 0x215e: 0x1100, 0x215f: 0x1100, 0x2160: 0x1100, 0x2161: 0x1100, 0x2162: 0x1100, 0x2163: 0x1100, - 0x2164: 0x1100, 0x2165: 0x1100, 0x2166: 0x1100, 0x2167: 0x1100, 0x2168: 0x1100, 0x2169: 0x1100, - 0x216a: 0x1100, 0x216b: 0x1100, 0x216c: 0x1100, 0x216d: 0x1100, 0x216e: 0x1100, 0x216f: 0x1100, - 0x2170: 0x1100, 0x2171: 0x1100, 0x2172: 0x1100, 0x2173: 0x1100, 0x2174: 0x1100, 0x2175: 0x1100, - 0x2176: 0x1100, 0x2177: 0x1100, 0x2178: 0x1100, 0x2179: 0x1100, 0x217a: 0x1100, 0x217b: 0x1100, - 0x217c: 0x1100, 0x217d: 0x1100, 0x217e: 0x1100, 0x217f: 0x1100, - // Block 0x86, offset 0x2180 - 0x2180: 0x1100, 0x2181: 0x1100, 0x2182: 0x1100, 0x2183: 0x1100, 0x2184: 0x1100, 0x2185: 0x1100, - 0x2186: 0x1100, 0x2187: 0x1100, 0x2188: 0x1100, 0x2189: 0x1100, 0x218a: 0x1100, 0x218b: 0x1100, - 0x218c: 0x1100, 0x218d: 0x1100, 0x218e: 0x1100, 0x218f: 0x1100, 0x2190: 0x1100, 0x2191: 0x1100, - 0x2192: 0x1100, 0x2193: 0x1100, 0x2194: 0x1100, 0x2195: 0x1100, 0x2196: 0x1100, 0x2197: 0x1100, - 0x2198: 0x1100, 0x2199: 0x1100, 0x219a: 0x1100, 0x219b: 0x1100, 0x219c: 0x1100, 0x219d: 0x1100, - 0x219e: 0x1100, 0x219f: 0x1100, 0x21a0: 0x1100, 0x21a1: 0x1100, 0x21a2: 0x1100, 0x21a3: 0x1100, - // Block 0x87, offset 0x21c0 - 0x21c0: 0x3300, 0x21c1: 0x3300, 0x21c2: 0x3300, 0x21c3: 0x3300, 0x21c4: 0x3300, 0x21c5: 0x3300, - 0x21c6: 0x3300, 0x21c7: 0x3300, 0x21c8: 0x3300, 0x21c9: 0x3300, 0x21ca: 0x3300, 0x21cb: 0x3300, - 0x21cc: 0x3300, 0x21cd: 0x3300, 0x21ce: 0x3300, 0x21cf: 0x3300, 0x21d0: 0x3300, 0x21d1: 0x3300, - 0x21d2: 0x3300, 0x21d3: 0x3300, 0x21d4: 0x3300, 0x21d5: 0x3300, 0x21d6: 0x3300, 0x21d7: 0x3300, - 0x21d8: 0x3300, 0x21d9: 0x3300, 0x21da: 0x3300, 0x21db: 0x3300, 0x21dc: 0x3300, 0x21dd: 0x3300, - 0x21de: 0x3300, 0x21df: 0x3300, 0x21e0: 0x3300, 0x21e1: 0x3300, 0x21e2: 0x3300, 0x21e3: 0x3300, - 0x21e4: 0x3300, 0x21e5: 0x3300, 0x21e6: 0x3300, 0x21e7: 0x3300, 0x21e8: 0x3300, 0x21e9: 0x3300, - 0x21ea: 0x3300, 0x21eb: 0x3300, 0x21ec: 0x3300, 0x21ed: 0x3300, 0x21ee: 0x3300, 0x21ef: 0x3300, - 0x21f0: 0x3300, 0x21f1: 0x3300, 0x21f2: 0x3300, 0x21f3: 0x3300, 0x21f4: 0x3300, 0x21f5: 0x3300, - 0x21f6: 0x3300, 0x21f7: 0x3300, 0x21f8: 0x3300, 0x21f9: 0x3300, 0x21fa: 0x3300, 0x21fb: 0x3300, - 0x21fc: 0x3300, 0x21fd: 0x3300, 0x21fe: 0x3300, 0x21ff: 0x3300, - // Block 0x88, offset 0x2200 - 0x2200: 0x3300, 0x2201: 0x3300, 0x2202: 0x3300, 0x2203: 0x3300, 0x2204: 0x3300, 0x2205: 0x3300, - 0x2206: 0x3300, 0x2207: 0x3300, 0x2208: 0x3300, 0x2209: 0x3300, 0x220a: 0x3300, 0x220b: 0x3300, - 0x220c: 0x3300, 0x220d: 0x3300, 0x2210: 0x3300, - 0x2212: 0x3300, 0x2215: 0x3300, 0x2216: 0x3300, 0x2217: 0x3300, - 0x2218: 0x3300, 0x2219: 0x3300, 0x221a: 0x3300, 0x221b: 0x3300, 0x221c: 0x3300, 0x221d: 0x3300, - 0x221e: 0x3300, 0x2220: 0x3300, 0x2222: 0x3300, - 0x2225: 0x3300, 0x2226: 0x3300, - 0x222a: 0x3300, 0x222b: 0x3300, 0x222c: 0x3300, 0x222d: 0x3300, - 0x2230: 0x3300, 0x2231: 0x3300, 0x2232: 0x3300, 0x2233: 0x3300, 0x2234: 0x3300, 0x2235: 0x3300, - 0x2236: 0x3300, 0x2237: 0x3300, 0x2238: 0x3300, 0x2239: 0x3300, 0x223a: 0x3300, 0x223b: 0x3300, - 0x223c: 0x3300, 0x223d: 0x3300, 0x223e: 0x3300, 0x223f: 0x3300, - // Block 0x89, offset 0x2240 - 0x2240: 0x3300, 0x2241: 0x3300, 0x2242: 0x3300, 0x2243: 0x3300, 0x2244: 0x3300, 0x2245: 0x3300, - 0x2246: 0x3300, 0x2247: 0x3300, 0x2248: 0x3300, 0x2249: 0x3300, 0x224a: 0x3300, 0x224b: 0x3300, - 0x224c: 0x3300, 0x224d: 0x3300, 0x224e: 0x3300, 0x224f: 0x3300, 0x2250: 0x3300, 0x2251: 0x3300, - 0x2252: 0x3300, 0x2253: 0x3300, 0x2254: 0x3300, 0x2255: 0x3300, 0x2256: 0x3300, 0x2257: 0x3300, - 0x2258: 0x3300, 0x2259: 0x3300, 0x225a: 0x3300, 0x225b: 0x3300, 0x225c: 0x3300, 0x225d: 0x3300, - 0x225e: 0x3300, 0x225f: 0x3300, 0x2260: 0x3300, 0x2261: 0x3300, 0x2262: 0x3300, 0x2263: 0x3300, - 0x2264: 0x3300, 0x2265: 0x3300, 0x2266: 0x3300, 0x2267: 0x3300, 0x2268: 0x3300, 0x2269: 0x3300, - 0x226a: 0x3300, 0x226b: 0x3300, 0x226c: 0x3300, 0x226d: 0x3300, - 0x2270: 0x3300, 0x2271: 0x3300, 0x2272: 0x3300, 0x2273: 0x3300, 0x2274: 0x3300, 0x2275: 0x3300, - 0x2276: 0x3300, 0x2277: 0x3300, 0x2278: 0x3300, 0x2279: 0x3300, 0x227a: 0x3300, 0x227b: 0x3300, - 0x227c: 0x3300, 0x227d: 0x3300, 0x227e: 0x3300, 0x227f: 0x3300, - // Block 0x8a, offset 0x2280 - 0x2280: 0x3300, 0x2281: 0x3300, 0x2282: 0x3300, 0x2283: 0x3300, 0x2284: 0x3300, 0x2285: 0x3300, - 0x2286: 0x3300, 0x2287: 0x3300, 0x2288: 0x3300, 0x2289: 0x3300, 0x228a: 0x3300, 0x228b: 0x3300, - 0x228c: 0x3300, 0x228d: 0x3300, 0x228e: 0x3300, 0x228f: 0x3300, 0x2290: 0x3300, 0x2291: 0x3300, - 0x2292: 0x3300, 0x2293: 0x3300, 0x2294: 0x3300, 0x2295: 0x3300, 0x2296: 0x3300, 0x2297: 0x3300, - 0x2298: 0x3300, 0x2299: 0x3300, - // Block 0x8b, offset 0x22c0 - 0x22c0: 0x3000, 0x22c1: 0x3000, 0x22c2: 0x3000, 0x22c3: 0x3000, 0x22c4: 0x3000, 0x22c5: 0x3000, - 0x22c6: 0x3000, - 0x22d3: 0x3000, 0x22d4: 0x3000, 0x22d5: 0x3000, 0x22d6: 0x3000, 0x22d7: 0x3000, - 0x22dd: 0x3300, - 0x22de: 0x001a, 0x22df: 0x3300, 0x22e0: 0x3000, 0x22e1: 0x3000, 0x22e2: 0x3000, 0x22e3: 0x3000, - 0x22e4: 0x3000, 0x22e5: 0x3000, 0x22e6: 0x3000, 0x22e7: 0x3000, 0x22e8: 0x3000, 0x22e9: 0x3000, - 0x22ea: 0x3300, 0x22eb: 0x3300, 0x22ec: 0x3300, 0x22ed: 0x3300, 0x22ee: 0x3300, 0x22ef: 0x3300, - 0x22f0: 0x3300, 0x22f1: 0x3300, 0x22f2: 0x3300, 0x22f3: 0x3300, 0x22f4: 0x3300, 0x22f5: 0x3300, - 0x22f6: 0x3300, 0x22f8: 0x3300, 0x22f9: 0x3300, 0x22fa: 0x3300, 0x22fb: 0x3300, - 0x22fc: 0x3300, 0x22fe: 0x3300, - // Block 0x8c, offset 0x2300 - 0x2300: 0x3300, 0x2301: 0x3300, 0x2303: 0x3300, 0x2304: 0x3300, - 0x2306: 0x3300, 0x2307: 0x3300, 0x2308: 0x3300, 0x2309: 0x3300, 0x230a: 0x3300, 0x230b: 0x3300, - 0x230c: 0x3300, 0x230d: 0x3300, 0x230e: 0x3300, 0x230f: 0x3000, 0x2310: 0x3000, 0x2311: 0x3000, - 0x2312: 0x3000, 0x2313: 0x3000, 0x2314: 0x3000, 0x2315: 0x3000, 0x2316: 0x3000, 0x2317: 0x3000, - 0x2318: 0x3000, 0x2319: 0x3000, 0x231a: 0x3000, 0x231b: 0x3000, 0x231c: 0x3000, 0x231d: 0x3000, - 0x231e: 0x3000, 0x231f: 0x3000, 0x2320: 0x3000, 0x2321: 0x3000, 0x2322: 0x3000, 0x2323: 0x3000, - 0x2324: 0x3000, 0x2325: 0x3000, 0x2326: 0x3000, 0x2327: 0x3000, 0x2328: 0x3000, 0x2329: 0x3000, - 0x232a: 0x3000, 0x232b: 0x3000, 0x232c: 0x3000, 0x232d: 0x3000, 0x232e: 0x3000, 0x232f: 0x3000, - 0x2330: 0x3000, 0x2331: 0x3000, 0x2332: 0x3000, 0x2333: 0x3000, 0x2334: 0x3000, 0x2335: 0x3000, - 0x2336: 0x3000, 0x2337: 0x3000, 0x2338: 0x3000, 0x2339: 0x3000, 0x233a: 0x3000, 0x233b: 0x3000, - 0x233c: 0x3000, 0x233d: 0x3000, 0x233e: 0x3000, 0x233f: 0x3000, - // Block 0x8d, offset 0x2340 - 0x2340: 0x3000, 0x2341: 0x3000, 0x2342: 0x3000, 0x2343: 0x3000, 0x2344: 0x3000, 0x2345: 0x3000, - 0x2346: 0x3000, 0x2347: 0x3000, 0x2348: 0x3000, 0x2349: 0x3000, 0x234a: 0x3000, 0x234b: 0x3000, - 0x234c: 0x3000, 0x234d: 0x3000, 0x234e: 0x3000, 0x234f: 0x3000, 0x2350: 0x3000, 0x2351: 0x3000, - 0x2352: 0x3000, 0x2353: 0x3000, 0x2354: 0x3000, 0x2355: 0x3000, 0x2356: 0x3000, 0x2357: 0x3000, - 0x2358: 0x3000, 0x2359: 0x3000, 0x235a: 0x3000, 0x235b: 0x3000, 0x235c: 0x3000, 0x235d: 0x3000, - 0x235e: 0x3000, 0x235f: 0x3000, 0x2360: 0x3000, 0x2361: 0x3000, 0x2362: 0x3000, 0x2363: 0x3000, - 0x2364: 0x3000, 0x2365: 0x3000, 0x2366: 0x3000, 0x2367: 0x3000, 0x2368: 0x3000, 0x2369: 0x3000, - 0x236a: 0x3000, 0x236b: 0x3000, 0x236c: 0x3000, 0x236d: 0x3000, 0x236e: 0x3000, 0x236f: 0x3000, - 0x2370: 0x3000, 0x2371: 0x3000, - // Block 0x8e, offset 0x2380 - 0x2393: 0x3000, 0x2394: 0x3000, 0x2395: 0x3000, 0x2396: 0x3000, 0x2397: 0x3000, - 0x2398: 0x3000, 0x2399: 0x3000, 0x239a: 0x3000, 0x239b: 0x3000, 0x239c: 0x3000, 0x239d: 0x3000, - 0x239e: 0x3000, 0x239f: 0x3000, 0x23a0: 0x3000, 0x23a1: 0x3000, 0x23a2: 0x3000, 0x23a3: 0x3000, - 0x23a4: 0x3000, 0x23a5: 0x3000, 0x23a6: 0x3000, 0x23a7: 0x3000, 0x23a8: 0x3000, 0x23a9: 0x3000, - 0x23aa: 0x3000, 0x23ab: 0x3000, 0x23ac: 0x3000, 0x23ad: 0x3000, 0x23ae: 0x3000, 0x23af: 0x3000, - 0x23b0: 0x3000, 0x23b1: 0x3000, 0x23b2: 0x3000, 0x23b3: 0x3000, 0x23b4: 0x3000, 0x23b5: 0x3000, - 0x23b6: 0x3000, 0x23b7: 0x3000, 0x23b8: 0x3000, 0x23b9: 0x3000, 0x23ba: 0x3000, 0x23bb: 0x3000, - 0x23bc: 0x3000, 0x23bd: 0x3000, 0x23be: 0x3000, 0x23bf: 0x3000, - // Block 0x8f, offset 0x23c0 - 0x23c0: 0x3000, 0x23c1: 0x3000, 0x23c2: 0x3000, 0x23c3: 0x3000, 0x23c4: 0x3000, 0x23c5: 0x3000, - 0x23c6: 0x3000, 0x23c7: 0x3000, 0x23c8: 0x3000, 0x23c9: 0x3000, 0x23ca: 0x3000, 0x23cb: 0x3000, - 0x23cc: 0x3000, 0x23cd: 0x3000, 0x23ce: 0x3000, 0x23cf: 0x3000, 0x23d0: 0x3000, 0x23d1: 0x3000, - 0x23d2: 0x3000, 0x23d3: 0x3000, 0x23d4: 0x3000, 0x23d5: 0x3000, 0x23d6: 0x3000, 0x23d7: 0x3000, - 0x23d8: 0x3000, 0x23d9: 0x3000, 0x23da: 0x3000, 0x23db: 0x3000, 0x23dc: 0x3000, 0x23dd: 0x3000, - 0x23de: 0x3000, 0x23df: 0x3000, 0x23e0: 0x3000, 0x23e1: 0x3000, 0x23e2: 0x3000, 0x23e3: 0x3000, - 0x23e4: 0x3000, 0x23e5: 0x3000, 0x23e6: 0x3000, 0x23e7: 0x3000, 0x23e8: 0x3000, 0x23e9: 0x3000, - 0x23ea: 0x3000, 0x23eb: 0x3000, 0x23ec: 0x3000, 0x23ed: 0x3000, 0x23ee: 0x3000, 0x23ef: 0x3000, - 0x23f0: 0x3000, 0x23f1: 0x3000, 0x23f2: 0x3000, 0x23f3: 0x3000, 0x23f4: 0x3000, 0x23f5: 0x3000, - 0x23f6: 0x3000, 0x23f7: 0x3000, 0x23f8: 0x3000, 0x23f9: 0x3000, 0x23fa: 0x3000, 0x23fb: 0x3000, - 0x23fc: 0x3000, 0x23fd: 0x3000, - // Block 0x90, offset 0x2400 - 0x2410: 0x3000, 0x2411: 0x3000, - 0x2412: 0x3000, 0x2413: 0x3000, 0x2414: 0x3000, 0x2415: 0x3000, 0x2416: 0x3000, 0x2417: 0x3000, - 0x2418: 0x3000, 0x2419: 0x3000, 0x241a: 0x3000, 0x241b: 0x3000, 0x241c: 0x3000, 0x241d: 0x3000, - 0x241e: 0x3000, 0x241f: 0x3000, 0x2420: 0x3000, 0x2421: 0x3000, 0x2422: 0x3000, 0x2423: 0x3000, - 0x2424: 0x3000, 0x2425: 0x3000, 0x2426: 0x3000, 0x2427: 0x3000, 0x2428: 0x3000, 0x2429: 0x3000, - 0x242a: 0x3000, 0x242b: 0x3000, 0x242c: 0x3000, 0x242d: 0x3000, 0x242e: 0x3000, 0x242f: 0x3000, - 0x2430: 0x3000, 0x2431: 0x3000, 0x2432: 0x3000, 0x2433: 0x3000, 0x2434: 0x3000, 0x2435: 0x3000, - 0x2436: 0x3000, 0x2437: 0x3000, 0x2438: 0x3000, 0x2439: 0x3000, 0x243a: 0x3000, 0x243b: 0x3000, - 0x243c: 0x3000, 0x243d: 0x3000, 0x243e: 0x3000, 0x243f: 0x3000, - // Block 0x91, offset 0x2440 - 0x2440: 0x3000, 0x2441: 0x3000, 0x2442: 0x3000, 0x2443: 0x3000, 0x2444: 0x3000, 0x2445: 0x3000, - 0x2446: 0x3000, 0x2447: 0x3000, 0x2448: 0x3000, 0x2449: 0x3000, 0x244a: 0x3000, 0x244b: 0x3000, - 0x244c: 0x3000, 0x244d: 0x3000, 0x244e: 0x3000, 0x244f: 0x3000, - 0x2452: 0x3000, 0x2453: 0x3000, 0x2454: 0x3000, 0x2455: 0x3000, 0x2456: 0x3000, 0x2457: 0x3000, - 0x2458: 0x3000, 0x2459: 0x3000, 0x245a: 0x3000, 0x245b: 0x3000, 0x245c: 0x3000, 0x245d: 0x3000, - 0x245e: 0x3000, 0x245f: 0x3000, 0x2460: 0x3000, 0x2461: 0x3000, 0x2462: 0x3000, 0x2463: 0x3000, - 0x2464: 0x3000, 0x2465: 0x3000, 0x2466: 0x3000, 0x2467: 0x3000, 0x2468: 0x3000, 0x2469: 0x3000, - 0x246a: 0x3000, 0x246b: 0x3000, 0x246c: 0x3000, 0x246d: 0x3000, 0x246e: 0x3000, 0x246f: 0x3000, - 0x2470: 0x3000, 0x2471: 0x3000, 0x2472: 0x3000, 0x2473: 0x3000, 0x2474: 0x3000, 0x2475: 0x3000, - 0x2476: 0x3000, 0x2477: 0x3000, 0x2478: 0x3000, 0x2479: 0x3000, 0x247a: 0x3000, 0x247b: 0x3000, - 0x247c: 0x3000, 0x247d: 0x3000, 0x247e: 0x3000, 0x247f: 0x3000, - // Block 0x92, offset 0x2480 - 0x2480: 0x3000, 0x2481: 0x3000, 0x2482: 0x3000, 0x2483: 0x3000, 0x2484: 0x3000, 0x2485: 0x3000, - 0x2486: 0x3000, 0x2487: 0x3000, - 0x24b0: 0x3000, 0x24b1: 0x3000, 0x24b2: 0x3000, 0x24b3: 0x3000, 0x24b4: 0x3000, 0x24b5: 0x3000, - 0x24b6: 0x3000, 0x24b7: 0x3000, 0x24b8: 0x3000, 0x24b9: 0x3000, 0x24ba: 0x3000, 0x24bb: 0x3000, - 0x24bc: 0x3000, - // Block 0x93, offset 0x24c0 - 0x24d0: 0x3000, 0x24d1: 0x3000, - 0x24d2: 0x3000, 0x24d3: 0x3000, 0x24d4: 0x3000, 0x24d5: 0x3000, 0x24d6: 0x3000, 0x24d7: 0x3000, - 0x24d8: 0x3000, 0x24d9: 0x3000, - 0x24e0: 0x00e6, 0x24e1: 0x00e6, 0x24e2: 0x00e6, 0x24e3: 0x00e6, - 0x24e4: 0x00e6, 0x24e5: 0x00e6, 0x24e6: 0x00e6, - 0x24f0: 0x3000, 0x24f1: 0x3000, 0x24f2: 0x3000, 0x24f3: 0x3000, 0x24f4: 0x3000, 0x24f5: 0x3000, - 0x24f6: 0x3000, 0x24f7: 0x3000, 0x24f8: 0x3000, 0x24f9: 0x3000, 0x24fa: 0x3000, 0x24fb: 0x3000, - 0x24fc: 0x3000, 0x24fd: 0x3000, 0x24fe: 0x3000, 0x24ff: 0x3000, - // Block 0x94, offset 0x2500 - 0x2500: 0x3000, 0x2501: 0x3000, 0x2502: 0x3000, 0x2503: 0x3000, 0x2504: 0x3000, - 0x2507: 0x3000, 0x2508: 0x3000, 0x2509: 0x3000, 0x250a: 0x3000, 0x250b: 0x3000, - 0x250c: 0x3000, 0x250d: 0x3000, 0x250e: 0x3000, 0x250f: 0x3000, 0x2510: 0x3000, 0x2511: 0x3000, - 0x2512: 0x3000, 0x2514: 0x3000, 0x2515: 0x3000, 0x2516: 0x3000, 0x2517: 0x3000, - 0x2518: 0x3000, 0x2519: 0x3000, 0x251a: 0x3000, 0x251b: 0x3000, 0x251c: 0x3000, 0x251d: 0x3000, - 0x251e: 0x3000, 0x251f: 0x3000, 0x2520: 0x3000, 0x2521: 0x3000, 0x2522: 0x3000, 0x2523: 0x3000, - 0x2524: 0x3000, 0x2525: 0x3000, 0x2526: 0x3000, 0x2528: 0x3000, 0x2529: 0x3000, - 0x252a: 0x3000, 0x252b: 0x3000, - 0x2530: 0x3000, 0x2531: 0x3000, 0x2532: 0x3000, 0x2534: 0x3000, - 0x2536: 0x3000, 0x2537: 0x3000, 0x2538: 0x3000, 0x2539: 0x3000, 0x253a: 0x3000, 0x253b: 0x3000, - 0x253c: 0x3000, 0x253d: 0x3000, 0x253e: 0x3000, 0x253f: 0x3000, - // Block 0x95, offset 0x2540 - 0x2540: 0x3000, 0x2541: 0x3000, 0x2542: 0x3000, 0x2543: 0x3000, 0x2544: 0x3000, 0x2545: 0x3000, - 0x2546: 0x3000, 0x2547: 0x3000, 0x2548: 0x3000, 0x2549: 0x3000, 0x254a: 0x3000, 0x254b: 0x3000, - 0x254c: 0x3000, 0x254d: 0x3000, 0x254e: 0x3000, 0x254f: 0x3000, 0x2550: 0x3000, 0x2551: 0x3000, - 0x2552: 0x3000, 0x2553: 0x3000, 0x2554: 0x3000, 0x2555: 0x3000, 0x2556: 0x3000, 0x2557: 0x3000, - 0x2558: 0x3000, 0x2559: 0x3000, 0x255a: 0x3000, 0x255b: 0x3000, 0x255c: 0x3000, 0x255d: 0x3000, - 0x255e: 0x3000, 0x255f: 0x3000, 0x2560: 0x3000, 0x2561: 0x3000, 0x2562: 0x3000, 0x2563: 0x3000, - 0x2564: 0x3000, 0x2565: 0x3000, 0x2566: 0x3000, 0x2567: 0x3000, 0x2568: 0x3000, 0x2569: 0x3000, - 0x256a: 0x3000, 0x256b: 0x3000, 0x256c: 0x3000, 0x256d: 0x3000, 0x256e: 0x3000, 0x256f: 0x3000, - 0x2570: 0x3000, 0x2571: 0x3000, 0x2572: 0x3000, 0x2573: 0x3000, 0x2574: 0x3000, 0x2575: 0x3000, - 0x2576: 0x3000, 0x2577: 0x3000, 0x2578: 0x3000, 0x2579: 0x3000, 0x257a: 0x3000, 0x257b: 0x3000, - 0x257c: 0x3000, - // Block 0x96, offset 0x2580 - 0x2581: 0x3000, 0x2582: 0x3000, 0x2583: 0x3000, 0x2584: 0x3000, 0x2585: 0x3000, - 0x2586: 0x3000, 0x2587: 0x3000, 0x2588: 0x3000, 0x2589: 0x3000, 0x258a: 0x3000, 0x258b: 0x3000, - 0x258c: 0x3000, 0x258d: 0x3000, 0x258e: 0x3000, 0x258f: 0x3000, 0x2590: 0x3000, 0x2591: 0x3000, - 0x2592: 0x3000, 0x2593: 0x3000, 0x2594: 0x3000, 0x2595: 0x3000, 0x2596: 0x3000, 0x2597: 0x3000, - 0x2598: 0x3000, 0x2599: 0x3000, 0x259a: 0x3000, 0x259b: 0x3000, 0x259c: 0x3000, 0x259d: 0x3000, - 0x259e: 0x3000, 0x259f: 0x3000, 0x25a0: 0x3000, 0x25a1: 0x3000, 0x25a2: 0x3000, 0x25a3: 0x3000, - 0x25a4: 0x3000, 0x25a5: 0x3000, 0x25a6: 0x3000, 0x25a7: 0x3000, 0x25a8: 0x3000, 0x25a9: 0x3000, - 0x25aa: 0x3000, 0x25ab: 0x3000, 0x25ac: 0x3000, 0x25ad: 0x3000, 0x25ae: 0x3000, 0x25af: 0x3000, - 0x25b0: 0x3000, 0x25b1: 0x3000, 0x25b2: 0x3000, 0x25b3: 0x3000, 0x25b4: 0x3000, 0x25b5: 0x3000, - 0x25b6: 0x3000, 0x25b7: 0x3000, 0x25b8: 0x3000, 0x25b9: 0x3000, 0x25ba: 0x3000, 0x25bb: 0x3000, - 0x25bc: 0x3000, 0x25bd: 0x3000, 0x25be: 0x3000, 0x25bf: 0x3000, - // Block 0x97, offset 0x25c0 - 0x25c2: 0x3000, 0x25c3: 0x3000, 0x25c4: 0x3000, 0x25c5: 0x3000, - 0x25c6: 0x3000, 0x25c7: 0x3000, 0x25ca: 0x3000, 0x25cb: 0x3000, - 0x25cc: 0x3000, 0x25cd: 0x3000, 0x25ce: 0x3000, 0x25cf: 0x3000, - 0x25d2: 0x3000, 0x25d3: 0x3000, 0x25d4: 0x3000, 0x25d5: 0x3000, 0x25d6: 0x3000, 0x25d7: 0x3000, - 0x25da: 0x3000, 0x25db: 0x3000, 0x25dc: 0x3000, - 0x25e0: 0x3000, 0x25e1: 0x3000, 0x25e2: 0x3000, 0x25e3: 0x3000, - 0x25e4: 0x3000, 0x25e5: 0x3000, 0x25e6: 0x3000, 0x25e8: 0x3000, 0x25e9: 0x3000, - 0x25ea: 0x3000, 0x25eb: 0x3000, 0x25ec: 0x3000, 0x25ed: 0x3000, 0x25ee: 0x3000, - // Block 0x98, offset 0x2600 - 0x263d: 0x00dc, - // Block 0x99, offset 0x2640 - 0x264d: 0x00dc, 0x264f: 0x00e6, - 0x2678: 0x00e6, 0x2679: 0x0001, 0x267a: 0x00dc, - 0x267f: 0x0009, - // Block 0x9a, offset 0x2680 - 0x2699: 0x8800, 0x269a: 0x1100, 0x269b: 0x8800, 0x269c: 0x1100, - 0x26a5: 0x8800, - 0x26ab: 0x1100, - 0x26b9: 0x0009, 0x26ba: 0x6607, - // Block 0x9b, offset 0x26c0 - 0x26de: 0x3300, 0x26df: 0x3300, 0x26e0: 0x3300, 0x26e1: 0x3300, 0x26e2: 0x3300, 0x26e3: 0x3300, - 0x26e4: 0x3300, 0x26e5: 0x00d8, 0x26e6: 0x00d8, 0x26e7: 0x0001, 0x26e8: 0x0001, 0x26e9: 0x0001, - 0x26ed: 0x00e2, 0x26ee: 0x00d8, 0x26ef: 0x00d8, - 0x26f0: 0x00d8, 0x26f1: 0x00d8, 0x26f2: 0x00d8, - 0x26fb: 0x00dc, - 0x26fc: 0x00dc, 0x26fd: 0x00dc, 0x26fe: 0x00dc, 0x26ff: 0x00dc, - // Block 0x9c, offset 0x2700 - 0x2700: 0x00dc, 0x2701: 0x00dc, 0x2702: 0x00dc, 0x2705: 0x00e6, - 0x2706: 0x00e6, 0x2707: 0x00e6, 0x2708: 0x00e6, 0x2709: 0x00e6, 0x270a: 0x00dc, 0x270b: 0x00dc, - 0x272a: 0x00e6, 0x272b: 0x00e6, 0x272c: 0x00e6, 0x272d: 0x00e6, - 0x273b: 0x3300, - 0x273c: 0x3300, 0x273d: 0x3300, 0x273e: 0x3300, 0x273f: 0x3300, - // Block 0x9d, offset 0x2740 - 0x2740: 0x3300, - // Block 0x9e, offset 0x2780 - 0x2782: 0x00e6, 0x2783: 0x00e6, 0x2784: 0x00e6, - // Block 0x9f, offset 0x27c0 - 0x27c0: 0x3000, 0x27c1: 0x3000, 0x27c2: 0x3000, 0x27c3: 0x3000, 0x27c4: 0x3000, 0x27c5: 0x3000, - 0x27c6: 0x3000, 0x27c7: 0x3000, 0x27c8: 0x3000, 0x27c9: 0x3000, 0x27ca: 0x3000, 0x27cb: 0x3000, - 0x27cc: 0x3000, 0x27cd: 0x3000, 0x27ce: 0x3000, 0x27cf: 0x3000, 0x27d0: 0x3000, 0x27d1: 0x3000, - 0x27d2: 0x3000, 0x27d3: 0x3000, 0x27d4: 0x3000, 0x27d6: 0x3000, 0x27d7: 0x3000, - 0x27d8: 0x3000, 0x27d9: 0x3000, 0x27da: 0x3000, 0x27db: 0x3000, 0x27dc: 0x3000, 0x27dd: 0x3000, - 0x27de: 0x3000, 0x27df: 0x3000, 0x27e0: 0x3000, 0x27e1: 0x3000, 0x27e2: 0x3000, 0x27e3: 0x3000, - 0x27e4: 0x3000, 0x27e5: 0x3000, 0x27e6: 0x3000, 0x27e7: 0x3000, 0x27e8: 0x3000, 0x27e9: 0x3000, - 0x27ea: 0x3000, 0x27eb: 0x3000, 0x27ec: 0x3000, 0x27ed: 0x3000, 0x27ee: 0x3000, 0x27ef: 0x3000, - 0x27f0: 0x3000, 0x27f1: 0x3000, 0x27f2: 0x3000, 0x27f3: 0x3000, 0x27f4: 0x3000, 0x27f5: 0x3000, - 0x27f6: 0x3000, 0x27f7: 0x3000, 0x27f8: 0x3000, 0x27f9: 0x3000, 0x27fa: 0x3000, 0x27fb: 0x3000, - 0x27fc: 0x3000, 0x27fd: 0x3000, 0x27fe: 0x3000, 0x27ff: 0x3000, - // Block 0xa0, offset 0x2800 - 0x2800: 0x3000, 0x2801: 0x3000, 0x2802: 0x3000, 0x2803: 0x3000, 0x2804: 0x3000, 0x2805: 0x3000, - 0x2806: 0x3000, 0x2807: 0x3000, 0x2808: 0x3000, 0x2809: 0x3000, 0x280a: 0x3000, 0x280b: 0x3000, - 0x280c: 0x3000, 0x280d: 0x3000, 0x280e: 0x3000, 0x280f: 0x3000, 0x2810: 0x3000, 0x2811: 0x3000, - 0x2812: 0x3000, 0x2813: 0x3000, 0x2814: 0x3000, 0x2815: 0x3000, 0x2816: 0x3000, 0x2817: 0x3000, - 0x2818: 0x3000, 0x2819: 0x3000, 0x281a: 0x3000, 0x281b: 0x3000, 0x281c: 0x3000, - 0x281e: 0x3000, 0x281f: 0x3000, 0x2822: 0x3000, - 0x2825: 0x3000, 0x2826: 0x3000, 0x2829: 0x3000, - 0x282a: 0x3000, 0x282b: 0x3000, 0x282c: 0x3000, 0x282e: 0x3000, 0x282f: 0x3000, - 0x2830: 0x3000, 0x2831: 0x3000, 0x2832: 0x3000, 0x2833: 0x3000, 0x2834: 0x3000, 0x2835: 0x3000, - 0x2836: 0x3000, 0x2837: 0x3000, 0x2838: 0x3000, 0x2839: 0x3000, 0x283b: 0x3000, - 0x283d: 0x3000, 0x283e: 0x3000, 0x283f: 0x3000, - // Block 0xa1, offset 0x2840 - 0x2840: 0x3000, 0x2841: 0x3000, 0x2842: 0x3000, 0x2843: 0x3000, 0x2845: 0x3000, - 0x2846: 0x3000, 0x2847: 0x3000, 0x2848: 0x3000, 0x2849: 0x3000, 0x284a: 0x3000, 0x284b: 0x3000, - 0x284c: 0x3000, 0x284d: 0x3000, 0x284e: 0x3000, 0x284f: 0x3000, 0x2850: 0x3000, 0x2851: 0x3000, - 0x2852: 0x3000, 0x2853: 0x3000, 0x2854: 0x3000, 0x2855: 0x3000, 0x2856: 0x3000, 0x2857: 0x3000, - 0x2858: 0x3000, 0x2859: 0x3000, 0x285a: 0x3000, 0x285b: 0x3000, 0x285c: 0x3000, 0x285d: 0x3000, - 0x285e: 0x3000, 0x285f: 0x3000, 0x2860: 0x3000, 0x2861: 0x3000, 0x2862: 0x3000, 0x2863: 0x3000, - 0x2864: 0x3000, 0x2865: 0x3000, 0x2866: 0x3000, 0x2867: 0x3000, 0x2868: 0x3000, 0x2869: 0x3000, - 0x286a: 0x3000, 0x286b: 0x3000, 0x286c: 0x3000, 0x286d: 0x3000, 0x286e: 0x3000, 0x286f: 0x3000, - 0x2870: 0x3000, 0x2871: 0x3000, 0x2872: 0x3000, 0x2873: 0x3000, 0x2874: 0x3000, 0x2875: 0x3000, - 0x2876: 0x3000, 0x2877: 0x3000, 0x2878: 0x3000, 0x2879: 0x3000, 0x287a: 0x3000, 0x287b: 0x3000, - 0x287c: 0x3000, 0x287d: 0x3000, 0x287e: 0x3000, 0x287f: 0x3000, - // Block 0xa2, offset 0x2880 - 0x2880: 0x3000, 0x2881: 0x3000, 0x2882: 0x3000, 0x2883: 0x3000, 0x2884: 0x3000, 0x2885: 0x3000, - 0x2887: 0x3000, 0x2888: 0x3000, 0x2889: 0x3000, 0x288a: 0x3000, - 0x288d: 0x3000, 0x288e: 0x3000, 0x288f: 0x3000, 0x2890: 0x3000, 0x2891: 0x3000, - 0x2892: 0x3000, 0x2893: 0x3000, 0x2894: 0x3000, 0x2896: 0x3000, 0x2897: 0x3000, - 0x2898: 0x3000, 0x2899: 0x3000, 0x289a: 0x3000, 0x289b: 0x3000, 0x289c: 0x3000, - 0x289e: 0x3000, 0x289f: 0x3000, 0x28a0: 0x3000, 0x28a1: 0x3000, 0x28a2: 0x3000, 0x28a3: 0x3000, - 0x28a4: 0x3000, 0x28a5: 0x3000, 0x28a6: 0x3000, 0x28a7: 0x3000, 0x28a8: 0x3000, 0x28a9: 0x3000, - 0x28aa: 0x3000, 0x28ab: 0x3000, 0x28ac: 0x3000, 0x28ad: 0x3000, 0x28ae: 0x3000, 0x28af: 0x3000, - 0x28b0: 0x3000, 0x28b1: 0x3000, 0x28b2: 0x3000, 0x28b3: 0x3000, 0x28b4: 0x3000, 0x28b5: 0x3000, - 0x28b6: 0x3000, 0x28b7: 0x3000, 0x28b8: 0x3000, 0x28b9: 0x3000, 0x28bb: 0x3000, - 0x28bc: 0x3000, 0x28bd: 0x3000, 0x28be: 0x3000, - // Block 0xa3, offset 0x28c0 - 0x28c0: 0x3000, 0x28c1: 0x3000, 0x28c2: 0x3000, 0x28c3: 0x3000, 0x28c4: 0x3000, - 0x28c6: 0x3000, 0x28ca: 0x3000, 0x28cb: 0x3000, - 0x28cc: 0x3000, 0x28cd: 0x3000, 0x28ce: 0x3000, 0x28cf: 0x3000, 0x28d0: 0x3000, - 0x28d2: 0x3000, 0x28d3: 0x3000, 0x28d4: 0x3000, 0x28d5: 0x3000, 0x28d6: 0x3000, 0x28d7: 0x3000, - 0x28d8: 0x3000, 0x28d9: 0x3000, 0x28da: 0x3000, 0x28db: 0x3000, 0x28dc: 0x3000, 0x28dd: 0x3000, - 0x28de: 0x3000, 0x28df: 0x3000, 0x28e0: 0x3000, 0x28e1: 0x3000, 0x28e2: 0x3000, 0x28e3: 0x3000, - 0x28e4: 0x3000, 0x28e5: 0x3000, 0x28e6: 0x3000, 0x28e7: 0x3000, 0x28e8: 0x3000, 0x28e9: 0x3000, - 0x28ea: 0x3000, 0x28eb: 0x3000, 0x28ec: 0x3000, 0x28ed: 0x3000, 0x28ee: 0x3000, 0x28ef: 0x3000, - 0x28f0: 0x3000, 0x28f1: 0x3000, 0x28f2: 0x3000, 0x28f3: 0x3000, 0x28f4: 0x3000, 0x28f5: 0x3000, - 0x28f6: 0x3000, 0x28f7: 0x3000, 0x28f8: 0x3000, 0x28f9: 0x3000, 0x28fa: 0x3000, 0x28fb: 0x3000, - 0x28fc: 0x3000, 0x28fd: 0x3000, 0x28fe: 0x3000, 0x28ff: 0x3000, - // Block 0xa4, offset 0x2900 - 0x2900: 0x3000, 0x2901: 0x3000, 0x2902: 0x3000, 0x2903: 0x3000, 0x2904: 0x3000, 0x2905: 0x3000, - 0x2906: 0x3000, 0x2907: 0x3000, 0x2908: 0x3000, 0x2909: 0x3000, 0x290a: 0x3000, 0x290b: 0x3000, - 0x290c: 0x3000, 0x290d: 0x3000, 0x290e: 0x3000, 0x290f: 0x3000, 0x2910: 0x3000, 0x2911: 0x3000, - 0x2912: 0x3000, 0x2913: 0x3000, 0x2914: 0x3000, 0x2915: 0x3000, 0x2916: 0x3000, 0x2917: 0x3000, - 0x2918: 0x3000, 0x2919: 0x3000, 0x291a: 0x3000, 0x291b: 0x3000, 0x291c: 0x3000, 0x291d: 0x3000, - 0x291e: 0x3000, 0x291f: 0x3000, 0x2920: 0x3000, 0x2921: 0x3000, 0x2922: 0x3000, 0x2923: 0x3000, - 0x2924: 0x3000, 0x2925: 0x3000, 0x2928: 0x3000, 0x2929: 0x3000, - 0x292a: 0x3000, 0x292b: 0x3000, 0x292c: 0x3000, 0x292d: 0x3000, 0x292e: 0x3000, 0x292f: 0x3000, - 0x2930: 0x3000, 0x2931: 0x3000, 0x2932: 0x3000, 0x2933: 0x3000, 0x2934: 0x3000, 0x2935: 0x3000, - 0x2936: 0x3000, 0x2937: 0x3000, 0x2938: 0x3000, 0x2939: 0x3000, 0x293a: 0x3000, 0x293b: 0x3000, - 0x293c: 0x3000, 0x293d: 0x3000, 0x293e: 0x3000, 0x293f: 0x3000, - // Block 0xa5, offset 0x2940 - 0x2940: 0x3000, 0x2941: 0x3000, 0x2942: 0x3000, 0x2943: 0x3000, 0x2944: 0x3000, 0x2945: 0x3000, - 0x2946: 0x3000, 0x2947: 0x3000, 0x2948: 0x3000, 0x2949: 0x3000, 0x294a: 0x3000, 0x294b: 0x3000, - 0x294e: 0x3000, 0x294f: 0x3000, 0x2950: 0x3000, 0x2951: 0x3000, - 0x2952: 0x3000, 0x2953: 0x3000, 0x2954: 0x3000, 0x2955: 0x3000, 0x2956: 0x3000, 0x2957: 0x3000, - 0x2958: 0x3000, 0x2959: 0x3000, 0x295a: 0x3000, 0x295b: 0x3000, 0x295c: 0x3000, 0x295d: 0x3000, - 0x295e: 0x3000, 0x295f: 0x3000, 0x2960: 0x3000, 0x2961: 0x3000, 0x2962: 0x3000, 0x2963: 0x3000, - 0x2964: 0x3000, 0x2965: 0x3000, 0x2966: 0x3000, 0x2967: 0x3000, 0x2968: 0x3000, 0x2969: 0x3000, - 0x296a: 0x3000, 0x296b: 0x3000, 0x296c: 0x3000, 0x296d: 0x3000, 0x296e: 0x3000, 0x296f: 0x3000, - 0x2970: 0x3000, 0x2971: 0x3000, 0x2972: 0x3000, 0x2973: 0x3000, 0x2974: 0x3000, 0x2975: 0x3000, - 0x2976: 0x3000, 0x2977: 0x3000, 0x2978: 0x3000, 0x2979: 0x3000, 0x297a: 0x3000, 0x297b: 0x3000, - 0x297c: 0x3000, 0x297d: 0x3000, 0x297e: 0x3000, 0x297f: 0x3000, - // Block 0xa6, offset 0x2980 - 0x2980: 0x3000, 0x2981: 0x3000, 0x2982: 0x3000, 0x2983: 0x3000, 0x2984: 0x3000, 0x2985: 0x3000, - 0x2986: 0x3000, 0x2987: 0x3000, 0x2988: 0x3000, 0x2989: 0x3000, 0x298a: 0x3000, - 0x2990: 0x3000, 0x2991: 0x3000, - 0x2992: 0x3000, 0x2993: 0x3000, 0x2994: 0x3000, 0x2995: 0x3000, 0x2996: 0x3000, 0x2997: 0x3000, - 0x2998: 0x3000, 0x2999: 0x3000, 0x299a: 0x3000, 0x299b: 0x3000, 0x299c: 0x3000, 0x299d: 0x3000, - 0x299e: 0x3000, 0x299f: 0x3000, 0x29a0: 0x3000, 0x29a1: 0x3000, 0x29a2: 0x3000, 0x29a3: 0x3000, - 0x29a4: 0x3000, 0x29a5: 0x3000, 0x29a6: 0x3000, 0x29a7: 0x3000, 0x29a8: 0x3000, 0x29a9: 0x3000, - 0x29aa: 0x3000, 0x29ab: 0x3000, 0x29ac: 0x3000, 0x29ad: 0x3000, 0x29ae: 0x3000, - 0x29b0: 0x3000, 0x29b1: 0x3000, 0x29b2: 0x3000, 0x29b3: 0x3000, 0x29b4: 0x3000, 0x29b5: 0x3000, - 0x29b6: 0x3000, 0x29b7: 0x3000, 0x29b8: 0x3000, 0x29b9: 0x3000, 0x29ba: 0x3000, 0x29bb: 0x3000, - 0x29bc: 0x3000, 0x29bd: 0x3000, 0x29be: 0x3000, 0x29bf: 0x3000, - // Block 0xa7, offset 0x29c0 - 0x29c0: 0x3000, 0x29c1: 0x3000, 0x29c2: 0x3000, 0x29c3: 0x3000, 0x29c4: 0x3000, 0x29c5: 0x3000, - 0x29c6: 0x3000, 0x29c7: 0x3000, 0x29c8: 0x3000, 0x29c9: 0x3000, 0x29ca: 0x3000, 0x29cb: 0x3000, - 0x29cc: 0x3000, 0x29cd: 0x3000, 0x29ce: 0x3000, 0x29cf: 0x3000, - // Block 0xa8, offset 0x2a00 - 0x2a10: 0x3000, - // Block 0xa9, offset 0x2a40 - 0x2a40: 0x3000, 0x2a41: 0x3000, 0x2a42: 0x3000, - 0x2a50: 0x3000, 0x2a51: 0x3000, - 0x2a52: 0x3000, 0x2a53: 0x3000, 0x2a54: 0x3000, 0x2a55: 0x3000, 0x2a56: 0x3000, 0x2a57: 0x3000, - 0x2a58: 0x3000, 0x2a59: 0x3000, 0x2a5a: 0x3000, 0x2a5b: 0x3000, 0x2a5c: 0x3000, 0x2a5d: 0x3000, - 0x2a5e: 0x3000, 0x2a5f: 0x3000, 0x2a60: 0x3000, 0x2a61: 0x3000, 0x2a62: 0x3000, 0x2a63: 0x3000, - 0x2a64: 0x3000, 0x2a65: 0x3000, 0x2a66: 0x3000, 0x2a67: 0x3000, 0x2a68: 0x3000, 0x2a69: 0x3000, - 0x2a6a: 0x3000, 0x2a6b: 0x3000, 0x2a6c: 0x3000, 0x2a6d: 0x3000, 0x2a6e: 0x3000, 0x2a6f: 0x3000, - 0x2a70: 0x3000, 0x2a71: 0x3000, 0x2a72: 0x3000, 0x2a73: 0x3000, 0x2a74: 0x3000, 0x2a75: 0x3000, - 0x2a76: 0x3000, 0x2a77: 0x3000, 0x2a78: 0x3000, 0x2a79: 0x3000, 0x2a7a: 0x3000, - // Block 0xaa, offset 0x2a80 - 0x2a80: 0x3000, 0x2a81: 0x3000, 0x2a82: 0x3000, 0x2a83: 0x3000, 0x2a84: 0x3000, 0x2a85: 0x3000, - 0x2a86: 0x3000, 0x2a87: 0x3000, 0x2a88: 0x3000, - 0x2a90: 0x3000, 0x2a91: 0x3000, - // Block 0xab, offset 0x2ac0 - 0x2ac0: 0x3300, 0x2ac1: 0x3300, 0x2ac2: 0x3300, 0x2ac3: 0x3300, 0x2ac4: 0x3300, 0x2ac5: 0x3300, - 0x2ac6: 0x3300, 0x2ac7: 0x3300, 0x2ac8: 0x3300, 0x2ac9: 0x3300, 0x2aca: 0x3300, 0x2acb: 0x3300, - 0x2acc: 0x3300, 0x2acd: 0x3300, 0x2ace: 0x3300, 0x2acf: 0x3300, 0x2ad0: 0x3300, 0x2ad1: 0x3300, - 0x2ad2: 0x3300, 0x2ad3: 0x3300, 0x2ad4: 0x3300, 0x2ad5: 0x3300, 0x2ad6: 0x3300, 0x2ad7: 0x3300, - 0x2ad8: 0x3300, 0x2ad9: 0x3300, 0x2ada: 0x3300, 0x2adb: 0x3300, 0x2adc: 0x3300, 0x2add: 0x3300, + 0x03c0: 0x1100, 0x03c1: 0x8800, 0x03c2: 0x1100, 0x03c4: 0x8800, 0x03c5: 0x1100, + 0x03c6: 0x8800, 0x03c7: 0x1100, 0x03c8: 0x8800, 0x03c9: 0x1100, + 0x03cf: 0x8800, 0x03d0: 0x1100, 0x03d1: 0x1100, + 0x03d2: 0x8800, 0x03d3: 0x1100, 0x03d4: 0x1100, 0x03d5: 0x8800, 0x03d6: 0x1100, 0x03d7: 0x1100, + 0x03d8: 0x8800, 0x03d9: 0x1100, 0x03da: 0x1100, 0x03db: 0x8800, 0x03dc: 0x1100, 0x03dd: 0x1100, + 0x03ef: 0x8800, + 0x03f0: 0x8800, 0x03f1: 0x8800, 0x03f2: 0x8800, 0x03f4: 0x1100, + 0x03f7: 0x1100, 0x03f8: 0x1100, 0x03f9: 0x1100, 0x03fa: 0x1100, + 0x03fd: 0x8800, 0x03fe: 0x1100, 0x03ff: 0x3000, +} + +// charInfoSparseOffset: 156 entries, 312 bytes +var charInfoSparseOffset = []uint16{0x0, 0x8, 0x13, 0x21, 0x25, 0x2f, 0x36, 0x39, 0x3c, 0x4a, 0x56, 0x58, 0x62, 0x67, 0x6e, 0x7d, 0x8a, 0x92, 0x96, 0x9b, 0x9d, 0xa5, 0xab, 0xae, 0xb5, 0xb9, 0xbd, 0xbf, 0xc1, 0xc8, 0xcc, 0xd1, 0xd7, 0xda, 0xe3, 0xe5, 0xed, 0xf1, 0xf3, 0xf6, 0xf9, 0xff, 0x10f, 0x11b, 0x11d, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x131, 0x134, 0x137, 0x139, 0x13c, 0x13f, 0x143, 0x152, 0x15a, 0x15c, 0x15f, 0x161, 0x16a, 0x16e, 0x172, 0x174, 0x183, 0x187, 0x18d, 0x195, 0x199, 0x1a2, 0x1ab, 0x1b6, 0x1bc, 0x1c0, 0x1ce, 0x1dd, 0x1e1, 0x1e8, 0x1ed, 0x1fc, 0x208, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x220, 0x222, 0x224, 0x226, 0x228, 0x231, 0x233, 0x236, 0x239, 0x23c, 0x23e, 0x241, 0x243, 0x245, 0x247, 0x24a, 0x24c, 0x24e, 0x250, 0x252, 0x258, 0x25a, 0x25c, 0x25e, 0x260, 0x262, 0x26c, 0x26f, 0x271, 0x27b, 0x280, 0x282, 0x284, 0x286, 0x288, 0x28b, 0x28e, 0x292, 0x29a, 0x29c, 0x29e, 0x2a5, 0x2a7, 0x2ae, 0x2b6, 0x2bd, 0x2c3, 0x2c5, 0x2c7, 0x2ca, 0x2d3, 0x2d6, 0x2dd, 0x2e2, 0x2e5, 0x2e8, 0x2ec, 0x2ee, 0x2f0, 0x2f3, 0x2f6} + +// charInfoSparseValues: 760 entries, 3040 bytes +var charInfoSparseValues = [760]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x07}, + {value: 0x3000, lo: 0xa0, hi: 0xa0}, + {value: 0x3800, lo: 0xa8, hi: 0xa8}, + {value: 0x3000, lo: 0xaa, hi: 0xaa}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb2, hi: 0xb5}, + {value: 0x3000, lo: 0xb8, hi: 0xba}, + {value: 0x3000, lo: 0xbc, hi: 0xbe}, + // Block 0x1, offset 0x2 + {value: 0x0000, lo: 0x0a}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x9900, lo: 0x82, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x8f}, + {value: 0x9900, lo: 0x92, hi: 0x93}, + {value: 0x1100, lo: 0x94, hi: 0xa5}, + {value: 0x1100, lo: 0xa8, hi: 0xb0}, + {value: 0x3000, lo: 0xb2, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb7}, + {value: 0x1100, lo: 0xb9, hi: 0xbe}, + {value: 0x3000, lo: 0xbf, hi: 0xbf}, + // Block 0x2, offset 0x3 + {value: 0x0000, lo: 0x0d}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x1100, lo: 0x83, hi: 0x88}, + {value: 0x3000, lo: 0x89, hi: 0x89}, + {value: 0x9900, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0x91}, + {value: 0x1100, lo: 0x94, hi: 0x99}, + {value: 0x9900, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0x9f}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x1100, lo: 0xa2, hi: 0xa5}, + {value: 0x9900, lo: 0xa8, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xbe}, + {value: 0x3800, lo: 0xbf, hi: 0xbf}, + // Block 0x3, offset 0x4 + {value: 0x0000, lo: 0x03}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x9900, lo: 0xaf, hi: 0xb0}, + {value: 0x8800, lo: 0xb7, hi: 0xb7}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x09}, + {value: 0x3000, lo: 0x84, hi: 0x8c}, + {value: 0x1100, lo: 0x8d, hi: 0x9c}, + {value: 0x1100, lo: 0x9e, hi: 0xa3}, + {value: 0x1100, lo: 0xa6, hi: 0xa9}, + {value: 0x9900, lo: 0xaa, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xb0}, + {value: 0x3000, lo: 0xb1, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb5}, + {value: 0x1100, lo: 0xb8, hi: 0xbf}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x06}, + {value: 0x1100, lo: 0x80, hi: 0x9b}, + {value: 0x1100, lo: 0x9e, hi: 0x9f}, + {value: 0x9900, lo: 0xa6, hi: 0xa9}, + {value: 0x1100, lo: 0xaa, hi: 0xad}, + {value: 0x9900, lo: 0xae, hi: 0xaf}, + {value: 0x1100, lo: 0xb0, hi: 0xb3}, + // Block 0x6, offset 0x7 + {value: 0x0000, lo: 0x02}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x3000, lo: 0xb0, hi: 0xb8}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x98, hi: 0x9d}, + {value: 0x3000, lo: 0xa0, hi: 0xa4}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x0d}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x8800, lo: 0x85, hi: 0x85}, + {value: 0x8800, lo: 0x89, hi: 0x89}, + {value: 0x9900, lo: 0x8a, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8d}, + {value: 0x9900, lo: 0x8e, hi: 0x8e}, + {value: 0x3000, lo: 0x90, hi: 0x91}, + {value: 0x3800, lo: 0x92, hi: 0x92}, + {value: 0x3100, lo: 0x93, hi: 0x94}, + {value: 0x3000, lo: 0x95, hi: 0x96}, + {value: 0x3000, lo: 0xb0, hi: 0xb2}, + {value: 0x3000, lo: 0xb4, hi: 0xb5}, + {value: 0x3000, lo: 0xb9, hi: 0xb9}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x0b}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x8800, lo: 0x8d, hi: 0x8d}, + {value: 0x1100, lo: 0x90, hi: 0x91}, + {value: 0x1100, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x96, hi: 0x96}, + {value: 0x1100, lo: 0x97, hi: 0x97}, + {value: 0x1100, lo: 0x9c, hi: 0x9e}, + {value: 0x8800, lo: 0xb4, hi: 0xb5}, + {value: 0x1100, lo: 0xb6, hi: 0xb7}, + // Block 0xa, offset 0xb + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x83, hi: 0x87}, + // Block 0xb, offset 0xc + {value: 0x0000, lo: 0x09}, + {value: 0x1100, lo: 0x81, hi: 0x82}, + {value: 0x1100, lo: 0x90, hi: 0x93}, + {value: 0x1100, lo: 0x96, hi: 0x97}, + {value: 0x8800, lo: 0x98, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9f}, + {value: 0x1100, lo: 0xa2, hi: 0xa7}, + {value: 0x8800, lo: 0xa8, hi: 0xa9}, + {value: 0x1100, lo: 0xaa, hi: 0xb5}, + {value: 0x1100, lo: 0xb8, hi: 0xb9}, + // Block 0xc, offset 0xd + {value: 0x0001, lo: 0x04}, + {value: 0x0018, lo: 0x81, hi: 0x82}, + {value: 0x00e6, lo: 0x84, hi: 0x84}, + {value: 0x00dc, lo: 0x85, hi: 0x85}, + {value: 0x0012, lo: 0x87, hi: 0x87}, + // Block 0xd, offset 0xe + {value: 0x0000, lo: 0x06}, + {value: 0x00e6, lo: 0x90, hi: 0x97}, + {value: 0x001e, lo: 0x98, hi: 0x98}, + {value: 0x001f, lo: 0x99, hi: 0x99}, + {value: 0x0020, lo: 0x9a, hi: 0x9a}, + {value: 0x1100, lo: 0xa2, hi: 0xa6}, + {value: 0x8800, lo: 0xa7, hi: 0xa7}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x0e}, + {value: 0x1100, lo: 0x80, hi: 0x80}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x1100, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x1100, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x95, hi: 0x95}, + {value: 0x00e6, lo: 0x96, hi: 0x9c}, + {value: 0x00e6, lo: 0x9f, hi: 0xa2}, + {value: 0x00dc, lo: 0xa3, hi: 0xa3}, + {value: 0x00e6, lo: 0xa4, hi: 0xa4}, + {value: 0x00e6, lo: 0xa7, hi: 0xa8}, + {value: 0x00dc, lo: 0xaa, hi: 0xaa}, + {value: 0x00e6, lo: 0xab, hi: 0xac}, + {value: 0x00dc, lo: 0xad, hi: 0xad}, + // Block 0xf, offset 0x10 + {value: 0x0000, lo: 0x0c}, + {value: 0x0024, lo: 0x91, hi: 0x91}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + {value: 0x00dc, lo: 0xb1, hi: 0xb1}, + {value: 0x00e6, lo: 0xb2, hi: 0xb3}, + {value: 0x00dc, lo: 0xb4, hi: 0xb4}, + {value: 0x00e6, lo: 0xb5, hi: 0xb6}, + {value: 0x00dc, lo: 0xb7, hi: 0xb9}, + {value: 0x00e6, lo: 0xba, hi: 0xba}, + {value: 0x00dc, lo: 0xbb, hi: 0xbc}, + {value: 0x00e6, lo: 0xbd, hi: 0xbd}, + {value: 0x00dc, lo: 0xbe, hi: 0xbe}, + {value: 0x00e6, lo: 0xbf, hi: 0xbf}, + // Block 0x10, offset 0x11 + {value: 0x000a, lo: 0x07}, + {value: 0x00e6, lo: 0x80, hi: 0x80}, + {value: 0x00e6, lo: 0x81, hi: 0x81}, + {value: 0x00dc, lo: 0x82, hi: 0x83}, + {value: 0x00dc, lo: 0x84, hi: 0x85}, + {value: 0x00dc, lo: 0x86, hi: 0x87}, + {value: 0x00dc, lo: 0x88, hi: 0x89}, + {value: 0x00e6, lo: 0x8a, hi: 0x8a}, + // Block 0x11, offset 0x12 + {value: 0x0000, lo: 0x03}, + {value: 0x00e6, lo: 0xab, hi: 0xb1}, + {value: 0x00dc, lo: 0xb2, hi: 0xb2}, + {value: 0x00e6, lo: 0xb3, hi: 0xb3}, + // Block 0x12, offset 0x13 + {value: 0x0000, lo: 0x04}, + {value: 0x00e6, lo: 0x96, hi: 0x99}, + {value: 0x00e6, lo: 0x9b, hi: 0xa3}, + {value: 0x00e6, lo: 0xa5, hi: 0xa7}, + {value: 0x00e6, lo: 0xa9, hi: 0xad}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x99, hi: 0x9b}, + // Block 0x14, offset 0x15 + {value: 0x7700, lo: 0x07}, + {value: 0x8800, lo: 0xa8, hi: 0xa8}, + {value: 0x1100, lo: 0xa9, hi: 0xa9}, + {value: 0x8800, lo: 0xb0, hi: 0xb0}, + {value: 0x1100, lo: 0xb1, hi: 0xb1}, + {value: 0x8800, lo: 0xb3, hi: 0xb3}, + {value: 0x1100, lo: 0xb4, hi: 0xb4}, + {value: 0x6607, lo: 0xbc, hi: 0xbc}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x05}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x00e6, lo: 0x91, hi: 0x91}, + {value: 0x00dc, lo: 0x92, hi: 0x92}, + {value: 0x00e6, lo: 0x93, hi: 0x94}, + {value: 0x3300, lo: 0x98, hi: 0x9f}, + // Block 0x16, offset 0x17 + {value: 0x65f9, lo: 0x02}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x17, offset 0x18 + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x1100, lo: 0x8b, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x97, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9d}, + {value: 0x3300, lo: 0x9f, hi: 0x9f}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x03}, + {value: 0x3300, lo: 0xb3, hi: 0xb3}, + {value: 0x3300, lo: 0xb6, hi: 0xb6}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + // Block 0x19, offset 0x1a + {value: 0x0000, lo: 0x03}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x3300, lo: 0x99, hi: 0x9b}, + {value: 0x3300, lo: 0x9e, hi: 0x9e}, + // Block 0x1a, offset 0x1b + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + // Block 0x1c, offset 0x1d + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x88}, + {value: 0x1100, lo: 0x8b, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x96, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9d}, + // Block 0x1d, offset 0x1e + {value: 0x5500, lo: 0x03}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x1100, lo: 0x94, hi: 0x94}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x04}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x8a, hi: 0x8c}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x97, hi: 0x97}, + // Block 0x1f, offset 0x20 + {value: 0x004b, lo: 0x05}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1100, lo: 0x88, hi: 0x88}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x0054, lo: 0x95, hi: 0x95}, + {value: 0x665b, lo: 0x96, hi: 0x96}, + // Block 0x20, offset 0x21 + {value: 0x87f9, lo: 0x02}, + {value: 0x0007, lo: 0xbc, hi: 0xbc}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x08}, + {value: 0x1100, lo: 0x80, hi: 0x80}, + {value: 0x6600, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1100, lo: 0x87, hi: 0x88}, + {value: 0x9900, lo: 0x8a, hi: 0x8a}, + {value: 0x1100, lo: 0x8b, hi: 0x8b}, + {value: 0x0009, lo: 0x8d, hi: 0x8d}, + {value: 0x6600, lo: 0x95, hi: 0x96}, + // Block 0x22, offset 0x23 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0xbe, hi: 0xbe}, + // Block 0x23, offset 0x24 + {value: 0x0000, lo: 0x07}, + {value: 0x6609, lo: 0x8a, hi: 0x8a}, + {value: 0x6600, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9a}, + {value: 0x9900, lo: 0x9c, hi: 0x9c}, + {value: 0x1100, lo: 0x9d, hi: 0x9e}, + {value: 0x6600, lo: 0x9f, hi: 0x9f}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + {value: 0x0067, lo: 0xb8, hi: 0xb9}, + {value: 0x0009, lo: 0xba, hi: 0xba}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x01}, + {value: 0x006b, lo: 0x88, hi: 0x8b}, + // Block 0x26, offset 0x27 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + {value: 0x0076, lo: 0xb8, hi: 0xb9}, + // Block 0x27, offset 0x28 + {value: 0x0000, lo: 0x02}, + {value: 0x007a, lo: 0x88, hi: 0x8b}, + {value: 0x3000, lo: 0x9c, hi: 0x9d}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x05}, + {value: 0x3000, lo: 0x8c, hi: 0x8c}, + {value: 0x00dc, lo: 0x98, hi: 0x99}, + {value: 0x00dc, lo: 0xb5, hi: 0xb5}, + {value: 0x00dc, lo: 0xb7, hi: 0xb7}, + {value: 0x00d8, lo: 0xb9, hi: 0xb9}, + // Block 0x29, offset 0x2a + {value: 0x0000, lo: 0x0f}, + {value: 0x3300, lo: 0x83, hi: 0x83}, + {value: 0x3300, lo: 0x8d, hi: 0x8d}, + {value: 0x3300, lo: 0x92, hi: 0x92}, + {value: 0x3300, lo: 0x97, hi: 0x97}, + {value: 0x3300, lo: 0x9c, hi: 0x9c}, + {value: 0x3300, lo: 0xa9, hi: 0xa9}, + {value: 0x0081, lo: 0xb1, hi: 0xb1}, + {value: 0x0082, lo: 0xb2, hi: 0xb2}, + {value: 0x3300, lo: 0xb3, hi: 0xb3}, + {value: 0x0084, lo: 0xb4, hi: 0xb4}, + {value: 0x3300, lo: 0xb5, hi: 0xb6}, + {value: 0x3000, lo: 0xb7, hi: 0xb7}, + {value: 0x3300, lo: 0xb8, hi: 0xb8}, + {value: 0x3000, lo: 0xb9, hi: 0xb9}, + {value: 0x0082, lo: 0xba, hi: 0xbd}, + // Block 0x2a, offset 0x2b + {value: 0x0000, lo: 0x0b}, + {value: 0x0082, lo: 0x80, hi: 0x80}, + {value: 0x3300, lo: 0x81, hi: 0x81}, + {value: 0x00e6, lo: 0x82, hi: 0x83}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0x86, hi: 0x87}, + {value: 0x3300, lo: 0x93, hi: 0x93}, + {value: 0x3300, lo: 0x9d, hi: 0x9d}, + {value: 0x3300, lo: 0xa2, hi: 0xa2}, + {value: 0x3300, lo: 0xa7, hi: 0xa7}, + {value: 0x3300, lo: 0xac, hi: 0xac}, + {value: 0x3300, lo: 0xb9, hi: 0xb9}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x86, hi: 0x86}, + // Block 0x2c, offset 0x2d + {value: 0x0000, lo: 0x05}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x1100, lo: 0xa6, hi: 0xa6}, + {value: 0x6600, lo: 0xae, hi: 0xae}, + {value: 0x0007, lo: 0xb7, hi: 0xb7}, + {value: 0x0009, lo: 0xb9, hi: 0xba}, + // Block 0x2d, offset 0x2e + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0x8d, hi: 0x8d}, + // Block 0x2e, offset 0x2f + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xbc, hi: 0xbc}, + // Block 0x2f, offset 0x30 + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0x80, hi: 0x92}, + // Block 0x30, offset 0x31 + {value: 0x0000, lo: 0x01}, + {value: 0xee00, lo: 0xa1, hi: 0xb5}, + // Block 0x31, offset 0x32 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0xa8, hi: 0xbf}, + // Block 0x32, offset 0x33 + {value: 0x0000, lo: 0x01}, + {value: 0x6600, lo: 0x80, hi: 0x82}, + // Block 0x33, offset 0x34 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x9d, hi: 0x9f}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x02}, + {value: 0x0009, lo: 0x94, hi: 0x94}, + {value: 0x0009, lo: 0xb4, hi: 0xb4}, + // Block 0x35, offset 0x36 + {value: 0x00dd, lo: 0x02}, + {value: 0x0009, lo: 0x92, hi: 0x92}, + {value: 0x00e6, lo: 0x9d, hi: 0x9d}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x00e4, lo: 0xa9, hi: 0xa9}, + // Block 0x37, offset 0x38 + {value: 0x0008, lo: 0x02}, + {value: 0x00de, lo: 0xb9, hi: 0xba}, + {value: 0x00dc, lo: 0xbb, hi: 0xbb}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x02}, + {value: 0x00e6, lo: 0x97, hi: 0x97}, + {value: 0x00dc, lo: 0x98, hi: 0x98}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x03}, + {value: 0x0009, lo: 0xa0, hi: 0xa0}, + {value: 0x00e6, lo: 0xb5, hi: 0xbc}, + {value: 0x00dc, lo: 0xbf, hi: 0xbf}, + // Block 0x3a, offset 0x3b + {value: 0x7700, lo: 0x0e}, + {value: 0x8800, lo: 0x85, hi: 0x85}, + {value: 0x1100, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x89}, + {value: 0x1100, lo: 0x8a, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0x8e}, + {value: 0x8800, lo: 0x91, hi: 0x91}, + {value: 0x1100, lo: 0x92, hi: 0x92}, + {value: 0x0007, lo: 0xb4, hi: 0xb4}, + {value: 0x6600, lo: 0xb5, hi: 0xb5}, + {value: 0x8800, lo: 0xba, hi: 0xba}, + {value: 0x1100, lo: 0xbb, hi: 0xbc}, + {value: 0x1100, lo: 0xbd, hi: 0xbe}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x3b, offset 0x3c + {value: 0x0000, lo: 0x07}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x82}, + {value: 0x1100, lo: 0x83, hi: 0x83}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0xab, hi: 0xab}, + {value: 0x00dc, lo: 0xac, hi: 0xac}, + {value: 0x00e6, lo: 0xad, hi: 0xb3}, + // Block 0x3c, offset 0x3d + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0xaa, hi: 0xaa}, + // Block 0x3d, offset 0x3e + {value: 0x0000, lo: 0x02}, + {value: 0x0007, lo: 0xa6, hi: 0xa6}, + {value: 0x0009, lo: 0xb2, hi: 0xb3}, + // Block 0x3e, offset 0x3f + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xb7, hi: 0xb7}, + // Block 0x3f, offset 0x40 + {value: 0x0000, lo: 0x08}, + {value: 0x00e6, lo: 0x90, hi: 0x92}, + {value: 0x0001, lo: 0x94, hi: 0x94}, + {value: 0x00dc, lo: 0x95, hi: 0x99}, + {value: 0x00e6, lo: 0x9a, hi: 0x9b}, + {value: 0x00dc, lo: 0x9c, hi: 0x9f}, + {value: 0x00e6, lo: 0xa0, hi: 0xa0}, + {value: 0x0001, lo: 0xa2, hi: 0xa8}, + {value: 0x00dc, lo: 0xad, hi: 0xad}, + // Block 0x40, offset 0x41 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0xac, hi: 0xae}, + {value: 0x3000, lo: 0xb0, hi: 0xba}, + {value: 0x3000, lo: 0xbc, hi: 0xbf}, + // Block 0x41, offset 0x42 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8d}, + {value: 0x3000, lo: 0x8f, hi: 0xaa}, + {value: 0x3000, lo: 0xb8, hi: 0xb8}, + // Block 0x42, offset 0x43 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x9b, hi: 0xbf}, + // Block 0x43, offset 0x44 + {value: 0x0000, lo: 0x0e}, + {value: 0x00e6, lo: 0x80, hi: 0x81}, + {value: 0x00dc, lo: 0x82, hi: 0x82}, + {value: 0x00e6, lo: 0x83, hi: 0x89}, + {value: 0x00dc, lo: 0x8a, hi: 0x8a}, + {value: 0x00e6, lo: 0x8b, hi: 0x8c}, + {value: 0x00ea, lo: 0x8d, hi: 0x8d}, + {value: 0x00d6, lo: 0x8e, hi: 0x8e}, + {value: 0x00dc, lo: 0x8f, hi: 0x8f}, + {value: 0x00ca, lo: 0x90, hi: 0x90}, + {value: 0x00e6, lo: 0x91, hi: 0xa6}, + {value: 0x00e9, lo: 0xbc, hi: 0xbc}, + {value: 0x00dc, lo: 0xbd, hi: 0xbd}, + {value: 0x00e6, lo: 0xbe, hi: 0xbe}, + {value: 0x00dc, lo: 0xbf, hi: 0xbf}, + // Block 0x44, offset 0x45 + {value: 0x0000, lo: 0x03}, + {value: 0x1100, lo: 0x80, hi: 0xb5}, + {value: 0x9900, lo: 0xb6, hi: 0xb7}, + {value: 0x1100, lo: 0xb8, hi: 0xbf}, + // Block 0x45, offset 0x46 + {value: 0x0000, lo: 0x05}, + {value: 0x1100, lo: 0x80, hi: 0x99}, + {value: 0x9900, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0xa1}, + {value: 0x9900, lo: 0xa2, hi: 0xa3}, + {value: 0x1100, lo: 0xa4, hi: 0xbf}, + // Block 0x46, offset 0x47 + {value: 0x0000, lo: 0x07}, + {value: 0x1100, lo: 0x80, hi: 0x99}, + {value: 0x3000, lo: 0x9a, hi: 0x9a}, + {value: 0x3100, lo: 0x9b, hi: 0x9b}, + {value: 0x9900, lo: 0xa0, hi: 0xa1}, + {value: 0x1100, lo: 0xa2, hi: 0xb7}, + {value: 0x9900, lo: 0xb8, hi: 0xb9}, + {value: 0x1100, lo: 0xba, hi: 0xbf}, + // Block 0x47, offset 0x48 + {value: 0x0000, lo: 0x03}, + {value: 0x1100, lo: 0x80, hi: 0x8b}, + {value: 0x9900, lo: 0x8c, hi: 0x8d}, + {value: 0x1100, lo: 0x8e, hi: 0xb9}, + // Block 0x48, offset 0x49 + {value: 0x0000, lo: 0x08}, + {value: 0x9900, lo: 0x80, hi: 0x91}, + {value: 0x1100, lo: 0x92, hi: 0x95}, + {value: 0x9900, lo: 0x98, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9d}, + {value: 0x9900, lo: 0xa0, hi: 0xb1}, + {value: 0x1100, lo: 0xb2, hi: 0xb7}, + {value: 0x9900, lo: 0xb8, hi: 0xb9}, + {value: 0x1100, lo: 0xba, hi: 0xbf}, + // Block 0x49, offset 0x4a + {value: 0x0000, lo: 0x08}, + {value: 0x1100, lo: 0x80, hi: 0xb4}, + {value: 0x9900, lo: 0xb6, hi: 0xb6}, + {value: 0x1100, lo: 0xb7, hi: 0xba}, + {value: 0x3300, lo: 0xbb, hi: 0xbb}, + {value: 0x1100, lo: 0xbc, hi: 0xbc}, + {value: 0x3000, lo: 0xbd, hi: 0xbd}, + {value: 0x3300, lo: 0xbe, hi: 0xbe}, + {value: 0x3800, lo: 0xbf, hi: 0xbf}, + // Block 0x4a, offset 0x4b + {value: 0x0000, lo: 0x0a}, + {value: 0x3300, lo: 0x80, hi: 0x81}, + {value: 0x3000, lo: 0x82, hi: 0x8a}, + {value: 0x3000, lo: 0x91, hi: 0x91}, + {value: 0x3000, lo: 0x97, hi: 0x97}, + {value: 0x3000, lo: 0xa4, hi: 0xa6}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb3, hi: 0xb4}, + {value: 0x3000, lo: 0xb6, hi: 0xb7}, + {value: 0x3000, lo: 0xbc, hi: 0xbc}, + {value: 0x3000, lo: 0xbe, hi: 0xbe}, + // Block 0x4b, offset 0x4c + {value: 0x0000, lo: 0x05}, + {value: 0x3000, lo: 0x87, hi: 0x89}, + {value: 0x3000, lo: 0x97, hi: 0x97}, + {value: 0x3000, lo: 0x9f, hi: 0x9f}, + {value: 0x3000, lo: 0xb0, hi: 0xb1}, + {value: 0x3000, lo: 0xb4, hi: 0xbf}, + // Block 0x4c, offset 0x4d + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8e}, + {value: 0x3000, lo: 0x90, hi: 0x9c}, + {value: 0x3000, lo: 0xa8, hi: 0xa8}, + // Block 0x4d, offset 0x4e + {value: 0x0000, lo: 0x0d}, + {value: 0x00e6, lo: 0x90, hi: 0x91}, + {value: 0x0001, lo: 0x92, hi: 0x93}, + {value: 0x00e6, lo: 0x94, hi: 0x97}, + {value: 0x0001, lo: 0x98, hi: 0x9a}, + {value: 0x00e6, lo: 0x9b, hi: 0x9c}, + {value: 0x00e6, lo: 0xa1, hi: 0xa1}, + {value: 0x0001, lo: 0xa5, hi: 0xa6}, + {value: 0x00e6, lo: 0xa7, hi: 0xa7}, + {value: 0x00dc, lo: 0xa8, hi: 0xa8}, + {value: 0x00e6, lo: 0xa9, hi: 0xa9}, + {value: 0x0001, lo: 0xaa, hi: 0xab}, + {value: 0x00dc, lo: 0xac, hi: 0xaf}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + // Block 0x4e, offset 0x4f + {value: 0x0000, lo: 0x0e}, + {value: 0x3000, lo: 0x80, hi: 0x83}, + {value: 0x3000, lo: 0x85, hi: 0x87}, + {value: 0x3000, lo: 0x89, hi: 0x93}, + {value: 0x3000, lo: 0x95, hi: 0x96}, + {value: 0x3000, lo: 0x99, hi: 0x9d}, + {value: 0x3000, lo: 0xa0, hi: 0xa2}, + {value: 0x3000, lo: 0xa4, hi: 0xa4}, + {value: 0x3300, lo: 0xa6, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xa8}, + {value: 0x3300, lo: 0xaa, hi: 0xab}, + {value: 0x3000, lo: 0xac, hi: 0xad}, + {value: 0x3000, lo: 0xaf, hi: 0xb1}, + {value: 0x3000, lo: 0xb3, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbf}, + // Block 0x4f, offset 0x50 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x3000, lo: 0x85, hi: 0x89}, + {value: 0x3000, lo: 0x90, hi: 0xbf}, + // Block 0x50, offset 0x51 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + {value: 0x1100, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0xae, hi: 0xae}, + // Block 0x51, offset 0x52 + {value: 0x0000, lo: 0x04}, + {value: 0x1100, lo: 0x8d, hi: 0x8f}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + // Block 0x52, offset 0x53 + {value: 0x7700, lo: 0x0e}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x84}, + {value: 0x8800, lo: 0x88, hi: 0x88}, + {value: 0x1100, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x1100, lo: 0x8c, hi: 0x8c}, + {value: 0x8800, lo: 0xa3, hi: 0xa3}, + {value: 0x1100, lo: 0xa4, hi: 0xa5}, + {value: 0x1100, lo: 0xa6, hi: 0xa6}, + {value: 0x3000, lo: 0xac, hi: 0xac}, + {value: 0x3000, lo: 0xad, hi: 0xad}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x3000, lo: 0xb0, hi: 0xb0}, + {value: 0x8800, lo: 0xbc, hi: 0xbc}, + // Block 0x53, offset 0x54 + {value: 0x0000, lo: 0x0b}, + {value: 0x1100, lo: 0x80, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x83}, + {value: 0x1100, lo: 0x84, hi: 0x85}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1100, lo: 0x88, hi: 0x89}, + {value: 0x8800, lo: 0x91, hi: 0x92}, + {value: 0x8800, lo: 0xa2, hi: 0xa2}, + {value: 0x8800, lo: 0xa8, hi: 0xa9}, + {value: 0x8800, lo: 0xab, hi: 0xab}, + {value: 0x1100, lo: 0xac, hi: 0xaf}, + {value: 0x8800, lo: 0xb2, hi: 0xb5}, + // Block 0x54, offset 0x55 + {value: 0x0000, lo: 0x02}, + {value: 0x1100, lo: 0xa0, hi: 0xa3}, + {value: 0x1100, lo: 0xaa, hi: 0xad}, + // Block 0x55, offset 0x56 + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0xa9, hi: 0xaa}, + // Block 0x56, offset 0x57 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xa0, hi: 0xbf}, + // Block 0x57, offset 0x58 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbf}, + // Block 0x58, offset 0x59 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xaa}, + // Block 0x59, offset 0x5a + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x8c, hi: 0x8c}, + // Block 0x5a, offset 0x5b + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb4, hi: 0xb6}, + // Block 0x5b, offset 0x5c + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x9c, hi: 0x9c}, + // Block 0x5c, offset 0x5d + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xbc, hi: 0xbd}, + // Block 0x5d, offset 0x5e + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xaf, hi: 0xb1}, + // Block 0x5e, offset 0x5f + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0xaf, hi: 0xaf}, + {value: 0x0009, lo: 0xbf, hi: 0xbf}, + // Block 0x5f, offset 0x60 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xa0, hi: 0xbf}, + // Block 0x60, offset 0x61 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x9f, hi: 0x9f}, + // Block 0x61, offset 0x62 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb3, hi: 0xb3}, + // Block 0x62, offset 0x63 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0x95}, + // Block 0x63, offset 0x64 + {value: 0x0000, lo: 0x08}, + {value: 0x3000, lo: 0x80, hi: 0x80}, + {value: 0x00da, lo: 0xaa, hi: 0xaa}, + {value: 0x00e4, lo: 0xab, hi: 0xab}, + {value: 0x00e8, lo: 0xac, hi: 0xac}, + {value: 0x00de, lo: 0xad, hi: 0xad}, + {value: 0x00e0, lo: 0xae, hi: 0xaf}, + {value: 0x3000, lo: 0xb6, hi: 0xb6}, + {value: 0x3000, lo: 0xb8, hi: 0xba}, + // Block 0x64, offset 0x65 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb1, hi: 0xbf}, + // Block 0x65, offset 0x66 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8e}, + {value: 0x3000, lo: 0x92, hi: 0x9f}, + // Block 0x66, offset 0x67 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x9e}, + {value: 0x3000, lo: 0xa0, hi: 0xbf}, + // Block 0x67, offset 0x68 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x87}, + {value: 0x3000, lo: 0x90, hi: 0xbe}, + // Block 0x68, offset 0x69 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbe}, + // Block 0x69, offset 0x6a + {value: 0x0000, lo: 0x02}, + {value: 0x00e6, lo: 0xaf, hi: 0xaf}, + {value: 0x00e6, lo: 0xbc, hi: 0xbd}, + // Block 0x6a, offset 0x6b + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0xb0, hi: 0xb1}, + // Block 0x6b, offset 0x6c + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0xb0, hi: 0xb0}, + // Block 0x6c, offset 0x6d + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x86, hi: 0x86}, + // Block 0x6d, offset 0x6e + {value: 0x0000, lo: 0x02}, + {value: 0x0009, lo: 0x84, hi: 0x84}, + {value: 0x00e6, lo: 0xa0, hi: 0xb1}, + // Block 0x6e, offset 0x6f + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0xab, hi: 0xad}, + // Block 0x6f, offset 0x70 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x93, hi: 0x93}, + // Block 0x70, offset 0x71 + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0xb3, hi: 0xb3}, + // Block 0x71, offset 0x72 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x80, hi: 0x80}, + // Block 0x72, offset 0x73 + {value: 0x0000, lo: 0x05}, + {value: 0x00e6, lo: 0xb0, hi: 0xb0}, + {value: 0x00e6, lo: 0xb2, hi: 0xb3}, + {value: 0x00dc, lo: 0xb4, hi: 0xb4}, + {value: 0x00e6, lo: 0xb7, hi: 0xb8}, + {value: 0x00e6, lo: 0xbe, hi: 0xbf}, + // Block 0x73, offset 0x74 + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x81, hi: 0x81}, + // Block 0x74, offset 0x75 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0xad, hi: 0xad}, + // Block 0x75, offset 0x76 + {value: 0x0000, lo: 0x01}, + {value: 0x1100, lo: 0x80, hi: 0xbf}, + // Block 0x76, offset 0x77 + {value: 0x0000, lo: 0x01}, + {value: 0x1100, lo: 0x80, hi: 0xa3}, + // Block 0x77, offset 0x78 + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0xbf}, + // Block 0x78, offset 0x79 + {value: 0x0000, lo: 0x09}, + {value: 0x3300, lo: 0x80, hi: 0x8d}, + {value: 0x3300, lo: 0x90, hi: 0x90}, + {value: 0x3300, lo: 0x92, hi: 0x92}, + {value: 0x3300, lo: 0x95, hi: 0x9e}, + {value: 0x3300, lo: 0xa0, hi: 0xa0}, + {value: 0x3300, lo: 0xa2, hi: 0xa2}, + {value: 0x3300, lo: 0xa5, hi: 0xa6}, + {value: 0x3300, lo: 0xaa, hi: 0xad}, + {value: 0x3300, lo: 0xb0, hi: 0xbf}, + // Block 0x79, offset 0x7a + {value: 0x0000, lo: 0x02}, + {value: 0x3300, lo: 0x80, hi: 0xad}, + {value: 0x3300, lo: 0xb0, hi: 0xbf}, + // Block 0x7a, offset 0x7b + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x99}, + // Block 0x7b, offset 0x7c + {value: 0x0000, lo: 0x09}, + {value: 0x3000, lo: 0x80, hi: 0x86}, + {value: 0x3000, lo: 0x93, hi: 0x97}, + {value: 0x3300, lo: 0x9d, hi: 0x9d}, + {value: 0x001a, lo: 0x9e, hi: 0x9e}, + {value: 0x3300, lo: 0x9f, hi: 0x9f}, + {value: 0x3000, lo: 0xa0, hi: 0xa9}, + {value: 0x3300, lo: 0xaa, hi: 0xb6}, + {value: 0x3300, lo: 0xb8, hi: 0xbc}, + {value: 0x3300, lo: 0xbe, hi: 0xbe}, + // Block 0x7c, offset 0x7d + {value: 0x0000, lo: 0x04}, + {value: 0x3300, lo: 0x80, hi: 0x81}, + {value: 0x3300, lo: 0x83, hi: 0x84}, + {value: 0x3300, lo: 0x86, hi: 0x8e}, + {value: 0x3000, lo: 0x8f, hi: 0xbf}, + // Block 0x7d, offset 0x7e + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xb1}, + // Block 0x7e, offset 0x7f + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x93, hi: 0xbf}, + // Block 0x7f, offset 0x80 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbd}, + // Block 0x80, offset 0x81 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x90, hi: 0xbf}, + // Block 0x81, offset 0x82 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8f}, + {value: 0x3000, lo: 0x92, hi: 0xbf}, + // Block 0x82, offset 0x83 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x87}, + {value: 0x3000, lo: 0xb0, hi: 0xbc}, + // Block 0x83, offset 0x84 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x90, hi: 0x99}, + {value: 0x00e6, lo: 0xa0, hi: 0xa6}, + {value: 0x3000, lo: 0xb0, hi: 0xbf}, + // Block 0x84, offset 0x85 + {value: 0x0000, lo: 0x07}, + {value: 0x3000, lo: 0x80, hi: 0x84}, + {value: 0x3000, lo: 0x87, hi: 0x92}, + {value: 0x3000, lo: 0x94, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xab}, + {value: 0x3000, lo: 0xb0, hi: 0xb2}, + {value: 0x3000, lo: 0xb4, hi: 0xb4}, + {value: 0x3000, lo: 0xb6, hi: 0xbf}, + // Block 0x85, offset 0x86 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0xbc}, + // Block 0x86, offset 0x87 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x81, hi: 0xbf}, + // Block 0x87, offset 0x88 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x82, hi: 0x87}, + {value: 0x3000, lo: 0x8a, hi: 0x8f}, + {value: 0x3000, lo: 0x92, hi: 0x97}, + {value: 0x3000, lo: 0x9a, hi: 0x9c}, + {value: 0x3000, lo: 0xa0, hi: 0xa6}, + {value: 0x3000, lo: 0xa8, hi: 0xae}, + // Block 0x88, offset 0x89 + {value: 0x0000, lo: 0x01}, + {value: 0x00dc, lo: 0xbd, hi: 0xbd}, + // Block 0x89, offset 0x8a + {value: 0x0000, lo: 0x06}, + {value: 0x00dc, lo: 0x8d, hi: 0x8d}, + {value: 0x00e6, lo: 0x8f, hi: 0x8f}, + {value: 0x00e6, lo: 0xb8, hi: 0xb8}, + {value: 0x0001, lo: 0xb9, hi: 0xb9}, + {value: 0x00dc, lo: 0xba, hi: 0xba}, + {value: 0x0009, lo: 0xbf, hi: 0xbf}, + // Block 0x8a, offset 0x8b + {value: 0x7700, lo: 0x07}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x1100, lo: 0x9a, hi: 0x9b}, + {value: 0x1100, lo: 0x9c, hi: 0x9c}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x1100, lo: 0xab, hi: 0xab}, + {value: 0x0009, lo: 0xb9, hi: 0xb9}, + {value: 0x6607, lo: 0xba, hi: 0xba}, + // Block 0x8b, offset 0x8c + {value: 0x0000, lo: 0x06}, + {value: 0x3300, lo: 0x9e, hi: 0xa4}, + {value: 0x00d8, lo: 0xa5, hi: 0xa6}, + {value: 0x0001, lo: 0xa7, hi: 0xa9}, + {value: 0x00e2, lo: 0xad, hi: 0xad}, + {value: 0x00d8, lo: 0xae, hi: 0xb2}, + {value: 0x00dc, lo: 0xbb, hi: 0xbf}, + // Block 0x8c, offset 0x8d + {value: 0x0000, lo: 0x05}, + {value: 0x00dc, lo: 0x80, hi: 0x82}, + {value: 0x00e6, lo: 0x85, hi: 0x89}, + {value: 0x00dc, lo: 0x8a, hi: 0x8b}, + {value: 0x00e6, lo: 0xaa, hi: 0xad}, + {value: 0x3300, lo: 0xbb, hi: 0xbf}, + // Block 0x8d, offset 0x8e + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x80}, + // Block 0x8e, offset 0x8f + {value: 0x0000, lo: 0x01}, + {value: 0x00e6, lo: 0x82, hi: 0x84}, + // Block 0x8f, offset 0x90 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x94}, + {value: 0x3000, lo: 0x96, hi: 0xbf}, + // Block 0x90, offset 0x91 + {value: 0x0000, lo: 0x08}, + {value: 0x3000, lo: 0x80, hi: 0x9c}, + {value: 0x3000, lo: 0x9e, hi: 0x9f}, + {value: 0x3000, lo: 0xa2, hi: 0xa2}, + {value: 0x3000, lo: 0xa5, hi: 0xa6}, + {value: 0x3000, lo: 0xa9, hi: 0xac}, + {value: 0x3000, lo: 0xae, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbb}, + {value: 0x3000, lo: 0xbd, hi: 0xbf}, + // Block 0x91, offset 0x92 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x83}, + {value: 0x3000, lo: 0x85, hi: 0xbf}, + // Block 0x92, offset 0x93 + {value: 0x0000, lo: 0x06}, + {value: 0x3000, lo: 0x80, hi: 0x85}, + {value: 0x3000, lo: 0x87, hi: 0x8a}, + {value: 0x3000, lo: 0x8d, hi: 0x94}, + {value: 0x3000, lo: 0x96, hi: 0x9c}, + {value: 0x3000, lo: 0x9e, hi: 0xb9}, + {value: 0x3000, lo: 0xbb, hi: 0xbe}, + // Block 0x93, offset 0x94 + {value: 0x0000, lo: 0x04}, + {value: 0x3000, lo: 0x80, hi: 0x84}, + {value: 0x3000, lo: 0x86, hi: 0x86}, + {value: 0x3000, lo: 0x8a, hi: 0x90}, + {value: 0x3000, lo: 0x92, hi: 0xbf}, + // Block 0x94, offset 0x95 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0xa5}, + {value: 0x3000, lo: 0xa8, hi: 0xbf}, + // Block 0x95, offset 0x96 + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x8b}, + {value: 0x3000, lo: 0x8e, hi: 0xbf}, + // Block 0x96, offset 0x97 + {value: 0x0000, lo: 0x03}, + {value: 0x3000, lo: 0x80, hi: 0x8a}, + {value: 0x3000, lo: 0x90, hi: 0xae}, + {value: 0x3000, lo: 0xb0, hi: 0xbf}, + // Block 0x97, offset 0x98 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x80, hi: 0x8f}, + // Block 0x98, offset 0x99 + {value: 0x0000, lo: 0x01}, + {value: 0x3000, lo: 0x90, hi: 0x90}, + // Block 0x99, offset 0x9a + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x82}, + {value: 0x3000, lo: 0x90, hi: 0xba}, + // Block 0x9a, offset 0x9b + {value: 0x0000, lo: 0x02}, + {value: 0x3000, lo: 0x80, hi: 0x88}, + {value: 0x3000, lo: 0x90, hi: 0x91}, + // Block 0x9b, offset 0x9c + {value: 0x0000, lo: 0x01}, + {value: 0x3300, lo: 0x80, hi: 0x9d}, } // charInfoLookup: 1152 bytes @@ -6511,36 +6689,36 @@ var charInfoLookup = [1152]uint8{ // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 - 0x0c2: 0x03, 0x0c3: 0x04, 0x0c4: 0x05, 0x0c5: 0x06, 0x0c6: 0x07, 0x0c7: 0x08, - 0x0c8: 0x09, 0x0ca: 0x0a, 0x0cb: 0x0b, 0x0cc: 0x0c, 0x0cd: 0x0d, 0x0ce: 0x0e, 0x0cf: 0x0f, - 0x0d0: 0x10, 0x0d1: 0x11, 0x0d2: 0x12, 0x0d3: 0x13, 0x0d6: 0x14, 0x0d7: 0x15, - 0x0d8: 0x16, 0x0d9: 0x17, 0x0db: 0x18, 0x0dc: 0x19, 0x0dd: 0x1a, 0x0df: 0x1b, + 0x0c2: 0x10, 0x0c3: 0x03, 0x0c4: 0x11, 0x0c5: 0x12, 0x0c6: 0x13, 0x0c7: 0x14, + 0x0c8: 0x15, 0x0ca: 0x16, 0x0cb: 0x17, 0x0cc: 0x04, 0x0cd: 0x05, 0x0ce: 0x06, 0x0cf: 0x18, + 0x0d0: 0x07, 0x0d1: 0x19, 0x0d2: 0x1a, 0x0d3: 0x1b, 0x0d6: 0x08, 0x0d7: 0x1c, + 0x0d8: 0x1d, 0x0d9: 0x09, 0x0db: 0x1e, 0x0dc: 0x1f, 0x0dd: 0x20, 0x0df: 0x21, 0x0e0: 0x04, 0x0e1: 0x05, 0x0e2: 0x06, 0x0e3: 0x07, 0x0ea: 0x08, 0x0eb: 0x09, 0x0ec: 0x09, 0x0ed: 0x0a, 0x0ef: 0x0b, 0x0f0: 0x11, // Block 0x4, offset 0x100 - 0x120: 0x1c, 0x121: 0x1d, 0x124: 0x1e, 0x125: 0x1f, 0x126: 0x20, 0x127: 0x21, - 0x128: 0x22, 0x129: 0x23, 0x12a: 0x24, 0x12b: 0x25, 0x12c: 0x20, 0x12d: 0x26, 0x12e: 0x27, 0x12f: 0x28, - 0x131: 0x29, 0x132: 0x2a, 0x133: 0x2b, 0x134: 0x2c, 0x135: 0x28, 0x137: 0x2d, - 0x138: 0x2e, 0x139: 0x2f, 0x13a: 0x30, 0x13b: 0x31, 0x13c: 0x32, 0x13d: 0x33, 0x13e: 0x34, 0x13f: 0x35, + 0x120: 0x22, 0x121: 0x23, 0x124: 0x24, 0x125: 0x25, 0x126: 0x26, 0x127: 0x27, + 0x128: 0x28, 0x129: 0x29, 0x12a: 0x2a, 0x12b: 0x2b, 0x12c: 0x26, 0x12d: 0x2c, 0x12e: 0x2d, 0x12f: 0x2e, + 0x131: 0x2f, 0x132: 0x30, 0x133: 0x31, 0x134: 0x32, 0x135: 0x2e, 0x137: 0x33, + 0x138: 0x34, 0x139: 0x35, 0x13a: 0x36, 0x13b: 0x37, 0x13c: 0x38, 0x13d: 0x39, 0x13e: 0x3a, 0x13f: 0x3b, // Block 0x5, offset 0x140 - 0x140: 0x36, 0x142: 0x37, 0x143: 0x38, 0x144: 0x39, 0x145: 0x3a, 0x146: 0x3b, 0x147: 0x3c, - 0x14d: 0x3d, - 0x15c: 0x3e, 0x15f: 0x3f, - 0x162: 0x40, 0x164: 0x41, - 0x168: 0x42, 0x169: 0x43, 0x16c: 0x44, 0x16d: 0x45, 0x16e: 0x46, 0x16f: 0x47, - 0x170: 0x48, 0x173: 0x49, 0x174: 0x4a, 0x175: 0x4b, 0x176: 0x4c, 0x177: 0x4d, - 0x178: 0x4e, 0x179: 0x4f, 0x17a: 0x50, 0x17b: 0x51, 0x17c: 0x52, 0x17d: 0x53, 0x17e: 0x54, 0x17f: 0x55, + 0x140: 0x3c, 0x142: 0x3d, 0x143: 0x3e, 0x144: 0x3f, 0x145: 0x40, 0x146: 0x41, 0x147: 0x42, + 0x14d: 0x43, + 0x15c: 0x44, 0x15f: 0x45, + 0x162: 0x46, 0x164: 0x47, + 0x168: 0x48, 0x169: 0x49, 0x16c: 0x4a, 0x16d: 0x4b, 0x16e: 0x4c, 0x16f: 0x4d, + 0x170: 0x4e, 0x173: 0x4f, 0x174: 0x50, 0x175: 0x51, 0x176: 0x52, 0x177: 0x53, + 0x178: 0x54, 0x179: 0x55, 0x17a: 0x56, 0x17b: 0x57, 0x17c: 0x58, 0x17d: 0x0a, 0x17e: 0x59, 0x17f: 0x0b, // Block 0x6, offset 0x180 - 0x180: 0x56, 0x181: 0x57, 0x182: 0x58, 0x183: 0x59, 0x184: 0x5a, 0x185: 0x5b, 0x186: 0x5c, 0x187: 0x5d, - 0x188: 0x5e, 0x189: 0x5f, 0x18a: 0x60, 0x18b: 0x61, 0x18c: 0x62, - 0x191: 0x63, 0x192: 0x64, 0x193: 0x65, - 0x1a8: 0x66, 0x1a9: 0x67, 0x1ab: 0x68, - 0x1b1: 0x69, 0x1b3: 0x6a, 0x1b5: 0x6b, 0x1b7: 0x6c, - 0x1ba: 0x6d, 0x1bb: 0x6e, 0x1bc: 0x64, 0x1bd: 0x64, 0x1be: 0x64, 0x1bf: 0x6f, + 0x180: 0x5a, 0x181: 0x5b, 0x182: 0x5c, 0x183: 0x5d, 0x184: 0x5e, 0x185: 0x5f, 0x186: 0x60, 0x187: 0x61, + 0x188: 0x62, 0x189: 0x0c, 0x18a: 0x63, 0x18b: 0x64, 0x18c: 0x65, + 0x191: 0x66, 0x192: 0x67, 0x193: 0x68, + 0x1a8: 0x69, 0x1a9: 0x6a, 0x1ab: 0x6b, + 0x1b1: 0x6c, 0x1b3: 0x6d, 0x1b5: 0x6e, 0x1b7: 0x6f, + 0x1ba: 0x70, 0x1bb: 0x71, 0x1bc: 0x67, 0x1bd: 0x67, 0x1be: 0x67, 0x1bf: 0x72, // Block 0x7, offset 0x1c0 - 0x1c0: 0x70, 0x1c1: 0x71, 0x1c2: 0x72, 0x1c3: 0x73, 0x1c4: 0x74, 0x1c5: 0x64, 0x1c6: 0x75, - 0x1c8: 0x76, 0x1c9: 0x77, 0x1ca: 0x64, 0x1cb: 0x78, 0x1cc: 0x64, 0x1cd: 0x64, 0x1ce: 0x64, 0x1cf: 0x64, + 0x1c0: 0x73, 0x1c1: 0x0d, 0x1c2: 0x0e, 0x1c3: 0x0f, 0x1c4: 0x74, 0x1c5: 0x67, 0x1c6: 0x75, + 0x1c8: 0x76, 0x1c9: 0x77, 0x1ca: 0x67, 0x1cb: 0x78, 0x1cc: 0x67, 0x1cd: 0x67, 0x1ce: 0x67, 0x1cf: 0x67, // Block 0x8, offset 0x200 0x219: 0x79, 0x21b: 0x7a, 0x21d: 0x7b, 0x220: 0x7c, 0x223: 0x7d, 0x224: 0x7e, 0x225: 0x7f, 0x226: 0x80, 0x227: 0x81, @@ -6564,8 +6742,8 @@ var charInfoLookup = [1152]uint8{ // Block 0xb, offset 0x2c0 0x2e4: 0x87, 0x2e5: 0x87, 0x2e6: 0x87, 0x2e7: 0x87, 0x2e8: 0x88, 0x2e9: 0x89, 0x2ea: 0x87, 0x2eb: 0x8a, 0x2ec: 0x8b, 0x2ed: 0x8c, 0x2ee: 0x8d, 0x2ef: 0x8e, - 0x2f0: 0x64, 0x2f1: 0x64, 0x2f2: 0x64, 0x2f3: 0x64, 0x2f4: 0x8f, 0x2f5: 0x90, 0x2f6: 0x91, 0x2f7: 0x92, - 0x2f8: 0x93, 0x2f9: 0x94, 0x2fa: 0x64, 0x2fb: 0x95, 0x2fc: 0x96, 0x2fd: 0x64, 0x2fe: 0x78, 0x2ff: 0x97, + 0x2f0: 0x67, 0x2f1: 0x67, 0x2f2: 0x67, 0x2f3: 0x67, 0x2f4: 0x8f, 0x2f5: 0x90, 0x2f6: 0x91, 0x2f7: 0x92, + 0x2f8: 0x93, 0x2f9: 0x94, 0x2fa: 0x67, 0x2fb: 0x95, 0x2fc: 0x96, 0x2fd: 0x67, 0x2fe: 0x78, 0x2ff: 0x97, // Block 0xc, offset 0x300 0x307: 0x98, 0x328: 0x99, @@ -6574,8 +6752,8 @@ var charInfoLookup = [1152]uint8{ // Block 0xe, offset 0x380 0x385: 0x9b, 0x386: 0x9c, 0x387: 0x9d, 0x389: 0x9e, - 0x390: 0x64, 0x391: 0x9f, 0x392: 0xa0, 0x393: 0xa1, 0x394: 0xa2, 0x395: 0xa3, 0x396: 0x64, 0x397: 0x64, - 0x398: 0x64, 0x399: 0x64, 0x39a: 0xa4, 0x39b: 0x64, 0x39c: 0x64, 0x39d: 0x64, 0x39e: 0x64, 0x39f: 0xa5, + 0x390: 0x67, 0x391: 0x9f, 0x392: 0xa0, 0x393: 0xa1, 0x394: 0xa2, 0x395: 0xa3, 0x396: 0x67, 0x397: 0x67, + 0x398: 0x67, 0x399: 0x67, 0x39a: 0xa4, 0x39b: 0x67, 0x39c: 0x67, 0x39d: 0x67, 0x39e: 0x67, 0x39f: 0xa5, // Block 0xf, offset 0x3c0 0x3c4: 0xa6, 0x3c5: 0xa7, 0x3c6: 0xa8, 0x3c8: 0xa9, 0x3c9: 0xaa, @@ -6588,6 +6766,6 @@ var charInfoLookup = [1152]uint8{ 0x46f: 0x10, } -var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:]} +var charInfoTrie = trie{charInfoLookup[:], charInfoValues[:], charInfoSparseValues[:], charInfoSparseOffset[:], 16} -// Total size of tables: 78KB (80362 bytes) +// Total size of tables: 48KB (48756 bytes) diff --git a/src/pkg/exp/norm/trie.go b/src/pkg/exp/norm/trie.go index edae2c212..93cb9c339 100644 --- a/src/pkg/exp/norm/trie.go +++ b/src/pkg/exp/norm/trie.go @@ -4,9 +4,44 @@ package norm +type valueRange struct { + value uint16 // header: value:stride + lo, hi byte // header: lo:n +} + type trie struct { - index []uint8 - values []uint16 + index []uint8 + values []uint16 + sparse []valueRange + sparseOffset []uint16 + cutoff uint8 // indices >= cutoff are sparse +} + +// lookupValue determines the type of block n and looks up the value for b. +// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block +// is a list of ranges with an accompanying value. Given a matching range r, +// the value for b is by r.value + (b - r.lo) * stride. +func (t *trie) lookupValue(n uint8, b byte) uint16 { + if n < t.cutoff { + return t.values[uint16(n)<<6+uint16(b&maskx)] + } + offset := t.sparseOffset[n-t.cutoff] + header := t.sparse[offset] + lo := offset + 1 + hi := lo + uint16(header.lo) + for lo < hi { + m := lo + (hi-lo)/2 + r := t.sparse[m] + if r.lo <= b && b <= r.hi { + return r.value + uint16(b-r.lo)*header.value + } + if b < r.lo { + hi = m + } else { + lo = m + 1 + } + } + return 0 } const ( @@ -44,8 +79,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c1 < tx || t2 <= c1 { return 0, 1 } - o := uint16(i)<<6 + uint16(c1)&maskx - return t.values[o], 2 + return t.lookupValue(i, c1), 2 case c0 < t4: if len(s) < 3 { return 0, 0 @@ -61,8 +95,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c2 < tx || t2 <= c2 { return 0, 2 } - o = uint16(i)<<6 + uint16(c2)&maskx - return t.values[o], 3 + return t.lookupValue(i, c2), 3 case c0 < t5: if len(s) < 4 { return 0, 0 @@ -84,8 +117,7 @@ func (t *trie) lookup(s []byte) (v uint16, sz int) { if c3 < tx || t2 <= c3 { return 0, 3 } - o = uint16(i)<<6 + uint16(c3)&maskx - return t.values[o], 4 + return t.lookupValue(i, c3), 4 } // Illegal rune return 0, 1 @@ -110,8 +142,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c1 < tx || t2 <= c1 { return 0, 1 } - o := uint16(i)<<6 + uint16(c1)&maskx - return t.values[o], 2 + return t.lookupValue(i, c1), 2 case c0 < t4: if len(s) < 3 { return 0, 0 @@ -127,8 +158,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c2 < tx || t2 <= c2 { return 0, 2 } - o = uint16(i)<<6 + uint16(c2)&maskx - return t.values[o], 3 + return t.lookupValue(i, c2), 3 case c0 < t5: if len(s) < 4 { return 0, 0 @@ -150,8 +180,7 @@ func (t *trie) lookupString(s string) (v uint16, sz int) { if c3 < tx || t2 <= c3 { return 0, 3 } - o = uint16(i)<<6 + uint16(c3)&maskx - return t.values[o], 4 + return t.lookupValue(i, c3), 4 } // Illegal rune return 0, 1 @@ -168,19 +197,16 @@ func (t *trie) lookupUnsafe(s []byte) uint16 { return 0 } i := t.index[c0] - o := uint16(i)<<6 + uint16(s[1])&maskx if c0 < t3 { - return t.values[o] + return t.lookupValue(i, s[1]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[2])&maskx + i = t.index[uint16(i)<<6+uint16(s[1])&maskx] if c0 < t4 { - return t.values[o] + return t.lookupValue(i, s[2]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[3])&maskx + i = t.index[uint16(i)<<6+uint16(s[2])&maskx] if c0 < t5 { - return t.values[o] + return t.lookupValue(i, s[3]) } return 0 } @@ -196,19 +222,16 @@ func (t *trie) lookupStringUnsafe(s string) uint16 { return 0 } i := t.index[c0] - o := uint16(i)<<6 + uint16(s[1])&maskx if c0 < t3 { - return t.values[o] + return t.lookupValue(i, s[1]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[2])&maskx + i = t.index[uint16(i)<<6+uint16(s[1])&maskx] if c0 < t4 { - return t.values[o] + return t.lookupValue(i, s[2]) } - i = t.index[o] - o = uint16(i)<<6 + uint16(s[3])&maskx + i = t.index[uint16(i)<<6+uint16(s[2])&maskx] if c0 < t5 { - return t.values[o] + return t.lookupValue(i, s[3]) } return 0 } diff --git a/src/pkg/exp/norm/trie_test.go b/src/pkg/exp/norm/trie_test.go index 6a309426e..5649fb7ee 100644 --- a/src/pkg/exp/norm/trie_test.go +++ b/src/pkg/exp/norm/trie_test.go @@ -8,6 +8,41 @@ import ( // Test data is located in triedata_test.go; generated by maketesttables. var testdata = testdataTrie +type rangeTest struct { + block uint8 + lookup byte + result uint16 + table []valueRange + offsets []uint16 +} + +var range1Off = []uint16{0, 2} +var range1 = []valueRange{ + {0, 1, 0}, + {1, 0x80, 0x80}, + {0, 2, 0}, + {1, 0x80, 0x80}, + {9, 0xff, 0xff}, +} + +var rangeTests = []rangeTest{ + {10, 0x80, 1, range1, range1Off}, + {10, 0x00, 0, range1, range1Off}, + {11, 0x80, 1, range1, range1Off}, + {11, 0xff, 9, range1, range1Off}, + {11, 0x00, 0, range1, range1Off}, +} + +func TestLookupSparse(t *testing.T) { + for i, test := range rangeTests { + n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10} + v := n.lookupValue(test.block, test.lookup) + if v != test.result { + t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result) + } + } +} + // Test cases for illegal runes. type trietest struct { size int @@ -49,10 +84,10 @@ func TestLookup(t *testing.T) { b, szg := mkUtf8(tt) v, szt := testdata.lookup(b) if int(v) != i { - t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i) + t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i) } if szt != szg { - t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg) + t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg) } } for i, tt := range tests { diff --git a/src/pkg/exp/norm/triedata_test.go b/src/pkg/exp/norm/triedata_test.go index f886e6004..e8898e5d4 100644 --- a/src/pkg/exp/norm/triedata_test.go +++ b/src/pkg/exp/norm/triedata_test.go @@ -4,34 +4,55 @@ package norm -var testRunes = []int{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111} +var testRunes = []int{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533} -// testdataValues: 768 entries, 1536 bytes +// testdataValues: 192 entries, 384 bytes // Block 2 is the null block. -var testdataValues = [768]uint16{ +var testdataValues = [192]uint16{ // Block 0x0, offset 0x0 0x000c: 0x0001, // Block 0x1, offset 0x40 0x007f: 0x0002, // Block 0x2, offset 0x80 - // Block 0x3, offset 0xc0 - 0x00c0: 0x0003, - // Block 0x4, offset 0x100 - 0x0100: 0x0004, - // Block 0x5, offset 0x140 - 0x017f: 0x0005, - // Block 0x6, offset 0x180 - 0x0180: 0x0006, - // Block 0x7, offset 0x1c0 - 0x01d9: 0x0007, - // Block 0x8, offset 0x200 - 0x023f: 0x0008, - // Block 0x9, offset 0x240 - 0x0240: 0x0009, - // Block 0xa, offset 0x280 - 0x0281: 0x000a, - // Block 0xb, offset 0x2c0 - 0x02ff: 0x000b, +} + +// testdataSparseOffset: 10 entries, 20 bytes +var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14} + +// testdataSparseValues: 22 entries, 88 bytes +var testdataSparseValues = [22]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x01}, + {value: 0x0003, lo: 0x80, hi: 0x80}, + // Block 0x1, offset 0x2 + {value: 0x0000, lo: 0x01}, + {value: 0x0004, lo: 0x80, hi: 0x80}, + // Block 0x2, offset 0x3 + {value: 0x0001, lo: 0x03}, + {value: 0x000c, lo: 0x80, hi: 0x82}, + {value: 0x000f, lo: 0x90, hi: 0x90}, + {value: 0x0010, lo: 0x95, hi: 0x95}, + // Block 0x3, offset 0x4 + {value: 0x0000, lo: 0x01}, + {value: 0x0005, lo: 0xbf, hi: 0xbf}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x01}, + {value: 0x0006, lo: 0x80, hi: 0x80}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0x99, hi: 0x99}, + // Block 0x6, offset 0x7 + {value: 0x0000, lo: 0x01}, + {value: 0x0008, lo: 0xbf, hi: 0xbf}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x80, hi: 0x80}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x01}, + {value: 0x000a, lo: 0x81, hi: 0x81}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x01}, + {value: 0x000b, lo: 0xbf, hi: 0xbf}, } // testdataLookup: 640 bytes @@ -42,22 +63,23 @@ var testdataLookup = [640]uint8{ // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0x0c2: 0x03, 0x0c4: 0x04, - 0x0df: 0x05, + 0x0c8: 0x05, + 0x0df: 0x06, 0x0e0: 0x04, 0x0ef: 0x05, 0x0f0: 0x07, 0x0f4: 0x09, // Block 0x4, offset 0x100 - 0x120: 0x06, 0x126: 0x07, + 0x120: 0x07, 0x126: 0x08, // Block 0x5, offset 0x140 - 0x17f: 0x08, + 0x17f: 0x09, // Block 0x6, offset 0x180 - 0x180: 0x09, 0x184: 0x0a, + 0x180: 0x0a, 0x184: 0x0b, // Block 0x7, offset 0x1c0 0x1d0: 0x06, // Block 0x8, offset 0x200 - 0x23f: 0x0b, + 0x23f: 0x0c, // Block 0x9, offset 0x240 0x24f: 0x08, } -var testdataTrie = trie{testdataLookup[:], testdataValues[:]} +var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 3} diff --git a/src/pkg/exp/norm/triegen.go b/src/pkg/exp/norm/triegen.go index 2b7eeee17..515e1c786 100644 --- a/src/pkg/exp/norm/triegen.go +++ b/src/pkg/exp/norm/triegen.go @@ -17,10 +17,13 @@ import ( "utf8" ) +const blockSize = 64 +const maxSparseEntries = 16 + // Intermediate trie structure type trieNode struct { table [256]*trieNode - value uint16 + value int b byte leaf bool } @@ -53,6 +56,44 @@ func (n trieNode) isInternal() bool { return internal } +func (n trieNode) mostFrequentStride() int { + counts := make(map[int]int) + v := 0 + for _, t := range n.table[0x80 : 0x80+blockSize] { + if t != nil { + if stride := t.value - v; v != 0 && stride >= 0 { + counts[stride]++ + } + v = t.value + } + } + var maxs, maxc int + for stride, cnt := range counts { + if cnt > maxc { + maxs, maxc = stride, cnt + } + } + return maxs +} + +func (n trieNode) countSparseEntries() int { + stride := n.mostFrequentStride() + var count, v int + for _, t := range n.table[0x80 : 0x80+blockSize] { + tv := 0 + if t != nil { + tv = t.value + } + if tv-v != stride { + if tv != 0 { + count++ + } + } + v = tv + } + return count +} + func (n *trieNode) insert(rune int, value uint16) { var p [utf8.UTFMax]byte sz := utf8.EncodeRune(p[:], rune) @@ -69,35 +110,40 @@ func (n *trieNode) insert(rune int, value uint16) { } n = nn } - n.value = value + n.value = int(value) n.leaf = true } type nodeIndex struct { lookupBlocks []*trieNode valueBlocks []*trieNode + sparseBlocks []*trieNode + sparseOffset []uint16 + sparseCount int - lookupBlockIdx map[uint32]uint16 - valueBlockIdx map[uint32]uint16 + lookupBlockIdx map[uint32]int + valueBlockIdx map[uint32]int } func newIndex() *nodeIndex { index := &nodeIndex{} index.lookupBlocks = make([]*trieNode, 0) index.valueBlocks = make([]*trieNode, 0) - index.lookupBlockIdx = make(map[uint32]uint16) - index.valueBlockIdx = make(map[uint32]uint16) + index.sparseBlocks = make([]*trieNode, 0) + index.sparseOffset = make([]uint16, 1) + index.lookupBlockIdx = make(map[uint32]int) + index.valueBlockIdx = make(map[uint32]int) return index } -func computeOffsets(index *nodeIndex, n *trieNode) uint16 { +func computeOffsets(index *nodeIndex, n *trieNode) int { if n.leaf { return n.value } hasher := crc32.New(crc32.MakeTable(crc32.IEEE)) // We only index continuation bytes. - for i := 0; i < 64; i++ { - var v uint16 = 0 + for i := 0; i < blockSize; i++ { + v := 0 if nn := n.table[0x80+i]; nn != nil { v = computeOffsets(index, nn) } @@ -107,7 +153,7 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { if n.isInternal() { v, ok := index.lookupBlockIdx[h] if !ok { - v = uint16(len(index.lookupBlocks)) + v = len(index.lookupBlocks) index.lookupBlocks = append(index.lookupBlocks, n) index.lookupBlockIdx[h] = v } @@ -115,9 +161,17 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { } else { v, ok := index.valueBlockIdx[h] if !ok { - v = uint16(len(index.valueBlocks)) - index.valueBlocks = append(index.valueBlocks, n) - index.valueBlockIdx[h] = v + if c := n.countSparseEntries(); c > maxSparseEntries { + v = len(index.valueBlocks) + index.valueBlocks = append(index.valueBlocks, n) + index.valueBlockIdx[h] = v + } else { + v = -len(index.sparseOffset) + index.sparseBlocks = append(index.sparseBlocks, n) + index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount)) + index.sparseCount += c + 1 + index.valueBlockIdx[h] = v + } } n.value = v } @@ -125,14 +179,14 @@ func computeOffsets(index *nodeIndex, n *trieNode) uint16 { } func printValueBlock(nr int, n *trieNode, offset int) { - boff := nr * 64 + boff := nr * blockSize fmt.Printf("\n// Block %#x, offset %#x", nr, boff) var printnewline bool - for i := 0; i < 64; i++ { + for i := 0; i < blockSize; i++ { if i%6 == 0 { printnewline = true } - v := uint16(0) + v := 0 if nn := n.table[i+offset]; nn != nil { v = nn.value } @@ -141,24 +195,55 @@ func printValueBlock(nr int, n *trieNode, offset int) { fmt.Printf("\n") printnewline = false } - fmt.Printf("%#04x:%#04x, ", nr*64+i, v) + fmt.Printf("%#04x:%#04x, ", boff+i, v) } } } -func printLookupBlock(nr int, n *trieNode, offset int) { - boff := nr * 64 +func printSparseBlock(nr int, n *trieNode) { + boff := -n.value + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + v := 0 + //stride := f(n) + stride := n.mostFrequentStride() + c := n.countSparseEntries() + fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c)) + for i, nn := range n.table[0x80 : 0x80+blockSize] { + nv := 0 + if nn != nil { + nv = nn.value + } + if nv-v != stride { + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+i-1) + } + if nv != 0 { + fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b) + } + } + v = nv + } + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+blockSize-1) + } +} + +func printLookupBlock(nr int, n *trieNode, offset, cutoff int) { + boff := nr * blockSize fmt.Printf("\n// Block %#x, offset %#x", nr, boff) var printnewline bool - for i := 0; i < 64; i++ { + for i := 0; i < blockSize; i++ { if i%8 == 0 { printnewline = true } - v := uint16(0) + v := 0 if nn := n.table[i+offset]; nn != nil { v = nn.value } if v != 0 { + if v < 0 { + v = -v - 1 + cutoff + } if printnewline { fmt.Printf("\n") printnewline = false @@ -182,7 +267,7 @@ func (t *trieNode) printTables(name string) int { } } - nv := len(index.valueBlocks) * 64 + nv := len(index.valueBlocks) * blockSize fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2) fmt.Printf("// Block 2 is the null block.\n") fmt.Printf("var %sValues = [%d]uint16 {", name, nv) @@ -194,18 +279,32 @@ func (t *trieNode) printTables(name string) int { } fmt.Print("\n}\n\n") - ni := len(index.lookupBlocks) * 64 + ls := len(index.sparseBlocks) + fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2) + fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:]) + + ns := index.sparseCount + fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4) + fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns) + for i, n := range index.sparseBlocks { + printSparseBlock(i, n) + } + fmt.Print("\n}\n\n") + + cutoff := len(index.valueBlocks) + ni := len(index.lookupBlocks) * blockSize fmt.Printf("// %sLookup: %d bytes\n", name, ni) fmt.Printf("// Block 0 is the null block.\n") fmt.Printf("var %sLookup = [%d]uint8 {", name, ni) - printLookupBlock(0, newNode(), 0) - printLookupBlock(1, newNode(), 0) - printLookupBlock(2, newNode(), 0) - printLookupBlock(3, t, 0xC0) + printLookupBlock(0, newNode(), 0, cutoff) + printLookupBlock(1, newNode(), 0, cutoff) + printLookupBlock(2, newNode(), 0, cutoff) + printLookupBlock(3, t, 0xC0, cutoff) for i := 4; i < len(index.lookupBlocks); i++ { - printLookupBlock(i, index.lookupBlocks[i], 0x80) + printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff) } fmt.Print("\n}\n\n") - fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:] }\n\n", name, name, name) - return nv*2 + ni + fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n", + name, name, name, name, name, cutoff) + return nv*2 + ns*4 + ni + ls*2 } diff --git a/src/pkg/exp/regexp/exec.go b/src/pkg/exp/regexp/exec.go index 88b16032e..43499a92f 100644 --- a/src/pkg/exp/regexp/exec.go +++ b/src/pkg/exp/regexp/exec.go @@ -128,6 +128,11 @@ func (m *machine) match(i input, pos int) bool { if width == 0 { break } + if len(m.matchcap) == 0 && m.matched { + // Found a match and not paying attention + // to where it is, so any match will do. + break + } pos += width rune, width = rune1, width1 if rune != endOfText { @@ -155,37 +160,37 @@ func (m *machine) clear(q *queue) { // which starts at position pos and ends at nextPos. // nextCond gives the setting for the empty-width flags after c. func (m *machine) step(runq, nextq *queue, pos, nextPos, c int, nextCond syntax.EmptyOp) { + longest := m.re.longest for j := 0; j < len(runq.dense); j++ { d := &runq.dense[j] t := d.t if t == nil { continue } - /* - * If we support leftmost-longest matching: - if longest && matched && match[0] < t.cap[0] { - m.free(t) - continue - } - */ - + if longest && m.matched && len(t.cap) > 0 && m.matchcap[0] < t.cap[0] { + m.free(t) + continue + } i := t.inst switch i.Op { default: panic("bad inst") case syntax.InstMatch: - if len(t.cap) > 0 { + if len(t.cap) > 0 && (!longest || !m.matched || m.matchcap[1] < pos) { t.cap[1] = pos copy(m.matchcap, t.cap) } - m.matched = true - for _, d := range runq.dense[j+1:] { - if d.t != nil { - m.free(d.t) + if !longest { + // First-match mode: cut off all lower-priority threads. + for _, d := range runq.dense[j+1:] { + if d.t != nil { + m.free(d.t) + } } + runq.dense = runq.dense[:0] } - runq.dense = runq.dense[:0] + m.matched = true case syntax.InstRune: if i.MatchRune(c) { diff --git a/src/pkg/exp/regexp/exec_test.go b/src/pkg/exp/regexp/exec_test.go index 15c4c532a..3adf8484c 100644 --- a/src/pkg/exp/regexp/exec_test.go +++ b/src/pkg/exp/regexp/exec_test.go @@ -6,9 +6,12 @@ package regexp import ( "bufio" - "compress/gzip" + "compress/bzip2" + "exp/regexp/syntax" "fmt" + "io" "os" + "path/filepath" "strconv" "strings" "testing" @@ -59,24 +62,34 @@ import ( // At time of writing, re2.txt is 32 MB but compresses to 760 kB, // so we store re2.txt.gz in the repository and decompress it on the fly. // -func TestRE2(t *testing.T) { +func TestRE2Search(t *testing.T) { + testRE2(t, "testdata/re2-search.txt") +} + +func TestRE2Exhaustive(t *testing.T) { if testing.Short() { - t.Log("skipping TestRE2 during short test") + t.Log("skipping TestRE2Exhaustive during short test") return } + testRE2(t, "testdata/re2-exhaustive.txt.bz2") +} - f, err := os.Open("re2.txt.gz") +func testRE2(t *testing.T, file string) { + f, err := os.Open(file) if err != nil { t.Fatal(err) } defer f.Close() - gz, err := gzip.NewReader(f) - if err != nil { - t.Fatalf("decompress re2.txt.gz: %v", err) + var txt io.Reader + if strings.HasSuffix(file, ".bz2") { + z := bzip2.NewReader(f) + txt = z + file = file[:len(file)-len(".bz2")] // for error messages + } else { + txt = f } - defer gz.Close() lineno := 0 - r := bufio.NewReader(gz) + r := bufio.NewReader(txt) var ( str []string input []string @@ -92,13 +105,13 @@ func TestRE2(t *testing.T) { if err == os.EOF { break } - t.Fatalf("re2.txt:%d: %v", lineno, err) + t.Fatalf("%s:%d: %v", file, lineno, err) } line = line[:len(line)-1] // chop \n lineno++ switch { case line == "": - t.Fatalf("re2.txt:%d: unexpected blank line", lineno) + t.Fatalf("%s:%d: unexpected blank line", file, lineno) case line[0] == '#': continue case 'A' <= line[0] && line[0] <= 'Z': @@ -114,7 +127,7 @@ func TestRE2(t *testing.T) { q, err := strconv.Unquote(line) if err != nil { // Fatal because we'll get out of sync. - t.Fatalf("re2.txt:%d: unquote %s: %v", lineno, line, err) + t.Fatalf("%s:%d: unquote %s: %v", file, lineno, line, err) } if inStrings { str = append(str, q) @@ -122,7 +135,7 @@ func TestRE2(t *testing.T) { } // Is a regexp. if len(input) != 0 { - t.Fatalf("re2.txt:%d: out of sync: have %d strings left before %#q", lineno, len(input), q) + t.Fatalf("%s:%d: out of sync: have %d strings left before %#q", file, lineno, len(input), q) } re, err = tryCompile(q) if err != nil { @@ -130,7 +143,7 @@ func TestRE2(t *testing.T) { // We don't and likely never will support \C; keep going. continue } - t.Errorf("re2.txt:%d: compile %#q: %v", lineno, q, err) + t.Errorf("%s:%d: compile %#q: %v", file, lineno, q, err) if nfail++; nfail >= 100 { t.Fatalf("stopping after %d errors", nfail) } @@ -140,7 +153,7 @@ func TestRE2(t *testing.T) { refull, err = tryCompile(full) if err != nil { // Fatal because q worked, so this should always work. - t.Fatalf("re2.txt:%d: compile full %#q: %v", lineno, full, err) + t.Fatalf("%s:%d: compile full %#q: %v", file, lineno, full, err) } input = str case line[0] == '-' || '0' <= line[0] && line[0] <= '9': @@ -151,7 +164,7 @@ func TestRE2(t *testing.T) { continue } if len(input) == 0 { - t.Fatalf("re2.txt:%d: out of sync: no input remaining", lineno) + t.Fatalf("%s:%d: out of sync: no input remaining", file, lineno) } var text string text, input = input[0], input[1:] @@ -164,39 +177,93 @@ func TestRE2(t *testing.T) { continue } res := strings.Split(line, ";") - if len(res) != 2 { - t.Fatalf("re2.txt:%d: have %d test results, want 2", lineno, len(res)) + if len(res) != len(run) { + t.Fatalf("%s:%d: have %d test results, want %d", file, lineno, len(res), len(run)) } - // res[0] is full match - // res[1] is partial match - // Run partial match first; don't bother with full if partial fails. - have := re.FindStringSubmatchIndex(text) - want := parseResult(t, lineno, res[1]) - if !same(have, want) { - t.Errorf("re2.txt:%d: %#q.FindSubmatchIndex(%#q) = %v, want %v", lineno, re, text, have, want) - if nfail++; nfail >= 100 { - t.Fatalf("stopping after %d errors", nfail) + for i := range res { + have, suffix := run[i](re, refull, text) + want := parseResult(t, file, lineno, res[i]) + if !same(have, want) { + t.Errorf("%s:%d: %#q%s.FindSubmatchIndex(%#q) = %v, want %v", file, lineno, re, suffix, text, have, want) + if nfail++; nfail >= 100 { + t.Fatalf("stopping after %d errors", nfail) + } + continue } - continue - } - have = refull.FindStringSubmatchIndex(text) - want = parseResult(t, lineno, res[0]) - if !same(have, want) { - t.Errorf("re2.txt:%d: %#q.FindSubmatchIndex(%#q) = %v, want %v", lineno, refull, text, have, want) - if nfail++; nfail >= 100 { - t.Fatalf("stopping after %d errors", nfail) + b, suffix := match[i](re, refull, text) + if b != (want != nil) { + t.Errorf("%s:%d: %#q%s.MatchString(%#q) = %v, want %v", file, lineno, re, suffix, text, b, !b) + if nfail++; nfail >= 100 { + t.Fatalf("stopping after %d errors", nfail) + } + continue } } + default: - t.Fatalf("re2.txt:%d: out of sync: %s\n", lineno, line) + t.Fatalf("%s:%d: out of sync: %s\n", file, lineno, line) } } if len(input) != 0 { - t.Fatalf("re2.txt:%d: out of sync: have %d strings left at EOF", lineno, len(input)) + t.Fatalf("%s:%d: out of sync: have %d strings left at EOF", file, lineno, len(input)) } t.Logf("%d cases tested", ncase) } +var run = []func(*Regexp, *Regexp, string) ([]int, string){ + runFull, + runPartial, + runFullLongest, + runPartialLongest, +} + +func runFull(re, refull *Regexp, text string) ([]int, string) { + refull.longest = false + return refull.FindStringSubmatchIndex(text), "[full]" +} + +func runPartial(re, refull *Regexp, text string) ([]int, string) { + re.longest = false + return re.FindStringSubmatchIndex(text), "" +} + +func runFullLongest(re, refull *Regexp, text string) ([]int, string) { + refull.longest = true + return refull.FindStringSubmatchIndex(text), "[full,longest]" +} + +func runPartialLongest(re, refull *Regexp, text string) ([]int, string) { + re.longest = true + return re.FindStringSubmatchIndex(text), "[longest]" +} + +var match = []func(*Regexp, *Regexp, string) (bool, string){ + matchFull, + matchPartial, + matchFullLongest, + matchPartialLongest, +} + +func matchFull(re, refull *Regexp, text string) (bool, string) { + refull.longest = false + return refull.MatchString(text), "[full]" +} + +func matchPartial(re, refull *Regexp, text string) (bool, string) { + re.longest = false + return re.MatchString(text), "" +} + +func matchFullLongest(re, refull *Regexp, text string) (bool, string) { + refull.longest = true + return refull.MatchString(text), "[full,longest]" +} + +func matchPartialLongest(re, refull *Regexp, text string) (bool, string) { + re.longest = true + return re.MatchString(text), "[longest]" +} + func isSingleBytes(s string) bool { for _, c := range s { if c >= utf8.RuneSelf { @@ -216,7 +283,7 @@ func tryCompile(s string) (re *Regexp, err os.Error) { return Compile(s) } -func parseResult(t *testing.T, lineno int, res string) []int { +func parseResult(t *testing.T, file string, lineno int, res string) []int { // A single - indicates no match. if res == "-" { return nil @@ -241,12 +308,12 @@ func parseResult(t *testing.T, lineno int, res string) []int { } else { k := strings.Index(pair, "-") if k < 0 { - t.Fatalf("re2.txt:%d: invalid pair %s", lineno, pair) + t.Fatalf("%s:%d: invalid pair %s", file, lineno, pair) } lo, err1 := strconv.Atoi(pair[:k]) hi, err2 := strconv.Atoi(pair[k+1:]) if err1 != nil || err2 != nil || lo > hi { - t.Fatalf("re2.txt:%d: invalid pair %s", lineno, pair) + t.Fatalf("%s:%d: invalid pair %s", file, lineno, pair) } out[n] = lo out[n+1] = hi @@ -269,3 +336,314 @@ func same(x, y []int) bool { } return true } + +// TestFowler runs this package's regexp API against the +// POSIX regular expression tests collected by Glenn Fowler +// at http://www2.research.att.com/~gsf/testregex/. +func TestFowler(t *testing.T) { + files, err := filepath.Glob("testdata/*.dat") + if err != nil { + t.Fatal(err) + } + for _, file := range files { + t.Log(file) + testFowler(t, file) + } +} + +var notab = MustCompilePOSIX(`[^\t]+`) + +func testFowler(t *testing.T, file string) { + f, err := os.Open(file) + if err != nil { + t.Error(err) + return + } + defer f.Close() + b := bufio.NewReader(f) + lineno := 0 + lastRegexp := "" +Reading: + for { + lineno++ + line, err := b.ReadString('\n') + if err != nil { + if err != os.EOF { + t.Errorf("%s:%d: %v", file, lineno, err) + } + break Reading + } + + // http://www2.research.att.com/~gsf/man/man1/testregex.html + // + // INPUT FORMAT + // Input lines may be blank, a comment beginning with #, or a test + // specification. A specification is five fields separated by one + // or more tabs. NULL denotes the empty string and NIL denotes the + // 0 pointer. + if line[0] == '#' || line[0] == '\n' { + continue Reading + } + line = line[:len(line)-1] + field := notab.FindAllString(line, -1) + for i, f := range field { + if f == "NULL" { + field[i] = "" + } + if f == "NIL" { + t.Logf("%s:%d: skip: %s", file, lineno, line) + continue Reading + } + } + if len(field) == 0 { + continue Reading + } + + // Field 1: the regex(3) flags to apply, one character per REG_feature + // flag. The test is skipped if REG_feature is not supported by the + // implementation. If the first character is not [BEASKLP] then the + // specification is a global control line. One or more of [BEASKLP] may be + // specified; the test will be repeated for each mode. + // + // B basic BRE (grep, ed, sed) + // E REG_EXTENDED ERE (egrep) + // A REG_AUGMENTED ARE (egrep with negation) + // S REG_SHELL SRE (sh glob) + // K REG_SHELL|REG_AUGMENTED KRE (ksh glob) + // L REG_LITERAL LRE (fgrep) + // + // a REG_LEFT|REG_RIGHT implicit ^...$ + // b REG_NOTBOL lhs does not match ^ + // c REG_COMMENT ignore space and #...\n + // d REG_SHELL_DOT explicit leading . match + // e REG_NOTEOL rhs does not match $ + // f REG_MULTIPLE multiple \n separated patterns + // g FNM_LEADING_DIR testfnmatch only -- match until / + // h REG_MULTIREF multiple digit backref + // i REG_ICASE ignore case + // j REG_SPAN . matches \n + // k REG_ESCAPE \ to ecape [...] delimiter + // l REG_LEFT implicit ^... + // m REG_MINIMAL minimal match + // n REG_NEWLINE explicit \n match + // o REG_ENCLOSED (|&) magic inside [@|&](...) + // p REG_SHELL_PATH explicit / match + // q REG_DELIMITED delimited pattern + // r REG_RIGHT implicit ...$ + // s REG_SHELL_ESCAPED \ not special + // t REG_MUSTDELIM all delimiters must be specified + // u standard unspecified behavior -- errors not counted + // v REG_CLASS_ESCAPE \ special inside [...] + // w REG_NOSUB no subexpression match array + // x REG_LENIENT let some errors slide + // y REG_LEFT regexec() implicit ^... + // z REG_NULL NULL subexpressions ok + // $ expand C \c escapes in fields 2 and 3 + // / field 2 is a regsubcomp() expression + // = field 3 is a regdecomp() expression + // + // Field 1 control lines: + // + // C set LC_COLLATE and LC_CTYPE to locale in field 2 + // + // ?test ... output field 5 if passed and != EXPECTED, silent otherwise + // &test ... output field 5 if current and previous passed + // |test ... output field 5 if current passed and previous failed + // ; ... output field 2 if previous failed + // {test ... skip if failed until } + // } end of skip + // + // : comment comment copied as output NOTE + // :comment:test :comment: ignored + // N[OTE] comment comment copied as output NOTE + // T[EST] comment comment + // + // number use number for nmatch (20 by default) + flag := field[0] + switch flag[0] { + case '?', '&', '|', ';', '{', '}': + // Ignore all the control operators. + // Just run everything. + flag = flag[1:] + if flag == "" { + continue Reading + } + case ':': + i := strings.Index(flag[1:], ":") + if i < 0 { + t.Logf("skip: %s", line) + continue Reading + } + flag = flag[1+i+1:] + case 'C', 'N', 'T', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + t.Logf("skip: %s", line) + continue Reading + } + + // Can check field count now that we've handled the myriad comment formats. + if len(field) < 4 { + t.Errorf("%s:%d: too few fields: %s", file, lineno, line) + continue Reading + } + + // Expand C escapes (a.k.a. Go escapes). + if strings.Contains(flag, "$") { + f := `"` + field[1] + `"` + if field[1], err = strconv.Unquote(f); err != nil { + t.Errorf("%s:%d: cannot unquote %s", file, lineno, f) + } + f = `"` + field[2] + `"` + if field[2], err = strconv.Unquote(f); err != nil { + t.Errorf("%s:%d: cannot unquote %s", file, lineno, f) + } + } + + // Field 2: the regular expression pattern; SAME uses the pattern from + // the previous specification. + // + if field[1] == "SAME" { + field[1] = lastRegexp + } + lastRegexp = field[1] + + // Field 3: the string to match. + text := field[2] + + // Field 4: the test outcome... + ok, shouldCompile, shouldMatch, pos := parseFowlerResult(field[3]) + if !ok { + t.Errorf("%s:%d: cannot parse result %#q", file, lineno, field[3]) + continue Reading + } + + // Field 5: optional comment appended to the report. + + Testing: + // Run test once for each specified capital letter mode that we support. + for _, c := range flag { + pattern := field[1] + syn := syntax.POSIX | syntax.ClassNL + switch c { + default: + continue Testing + case 'E': + // extended regexp (what we support) + case 'L': + // literal + pattern = QuoteMeta(pattern) + } + + for _, c := range flag { + switch c { + case 'i': + syn |= syntax.FoldCase + } + } + + re, err := compile(pattern, syn, true) + if err != nil { + if shouldCompile { + t.Errorf("%s:%d: %#q did not compile", file, lineno, pattern) + } + continue Testing + } + if !shouldCompile { + t.Errorf("%s:%d: %#q should not compile", file, lineno, pattern) + continue Testing + } + match := re.MatchString(text) + if match != shouldMatch { + t.Errorf("%s:%d: %#q.Match(%#q) = %v, want %v", file, lineno, pattern, text, match, shouldMatch) + continue Testing + } + have := re.FindStringSubmatchIndex(text) + if (len(have) > 0) != match { + t.Errorf("%s:%d: %#q.Match(%#q) = %v, but %#q.FindSubmatchIndex(%#q) = %v", file, lineno, pattern, text, match, pattern, text, have) + continue Testing + } + if len(have) > len(pos) { + have = have[:len(pos)] + } + if !same(have, pos) { + t.Errorf("%s:%d: %#q.FindSubmatchIndex(%#q) = %v, want %v", file, lineno, pattern, text, have, pos) + } + } + } +} + +func parseFowlerResult(s string) (ok, compiled, matched bool, pos []int) { + // Field 4: the test outcome. This is either one of the posix error + // codes (with REG_ omitted) or the match array, a list of (m,n) + // entries with m and n being first and last+1 positions in the + // field 3 string, or NULL if REG_NOSUB is in effect and success + // is expected. BADPAT is acceptable in place of any regcomp(3) + // error code. The match[] array is initialized to (-2,-2) before + // each test. All array elements from 0 to nmatch-1 must be specified + // in the outcome. Unspecified endpoints (offset -1) are denoted by ?. + // Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a + // matched (?{...}) expression, where x is the text enclosed by {...}, + // o is the expression ordinal counting from 1, and n is the length of + // the unmatched portion of the subject string. If x starts with a + // number then that is the return value of re_execf(), otherwise 0 is + // returned. + switch { + case s == "": + // Match with no position information. + ok = true + compiled = true + matched = true + return + case s == "NOMATCH": + // Match failure. + ok = true + compiled = true + matched = false + return + case 'A' <= s[0] && s[0] <= 'Z': + // All the other error codes are compile errors. + ok = true + compiled = false + return + } + compiled = true + + var x []int + for s != "" { + var end byte = ')' + if len(x)%2 == 0 { + if s[0] != '(' { + ok = false + return + } + s = s[1:] + end = ',' + } + i := 0 + for i < len(s) && s[i] != end { + i++ + } + if i == 0 || i == len(s) { + ok = false + return + } + var v = -1 + var err os.Error + if s[:i] != "?" { + v, err = strconv.Atoi(s[:i]) + if err != nil { + ok = false + return + } + } + x = append(x, v) + s = s[i+1:] + } + if len(x)%2 != 0 { + ok = false + return + } + ok = true + matched = true + pos = x + return +} diff --git a/src/pkg/exp/regexp/find_test.go b/src/pkg/exp/regexp/find_test.go index 6406bb6e6..e07eb7d5c 100644 --- a/src/pkg/exp/regexp/find_test.go +++ b/src/pkg/exp/regexp/find_test.go @@ -98,6 +98,15 @@ var findTests = []FindTest{ {`\B`, "x y", nil}, {`\B`, "xx yy", build(2, 1, 1, 4, 4)}, + // RE2 tests + {`[^\S\s]`, "abcd", nil}, + {`[^\S[:space:]]`, "abcd", nil}, + {`[^\D\d]`, "abcd", nil}, + {`[^\D[:digit:]]`, "abcd", nil}, + {`(?i)\W`, "x", nil}, + {`(?i)\W`, "k", nil}, + {`(?i)\W`, "s", nil}, + // can backslash-escape any punctuation {`\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\{\|\}\~`, `!"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, build(1, 0, 31)}, diff --git a/src/pkg/exp/regexp/re2.txt.gz b/src/pkg/exp/regexp/re2.txt.gz Binary files differdeleted file mode 100644 index 2b8c832e5..000000000 --- a/src/pkg/exp/regexp/re2.txt.gz +++ /dev/null diff --git a/src/pkg/exp/regexp/regexp.go b/src/pkg/exp/regexp/regexp.go index 11feecd55..7480b098c 100644 --- a/src/pkg/exp/regexp/regexp.go +++ b/src/pkg/exp/regexp/regexp.go @@ -58,6 +58,7 @@ import ( "exp/regexp/syntax" "io" "os" + "strconv" "strings" "sync" "utf8" @@ -85,6 +86,7 @@ type Regexp struct { prefixRune int // first rune in prefix cond syntax.EmptyOp // empty-width conditions required at start of match numSubexp int + longest bool // cache of machines for running regexp mu sync.Mutex @@ -96,10 +98,45 @@ func (re *Regexp) String() string { return re.expr } -// Compile parses a regular expression and returns, if successful, a Regexp -// object that can be used to match against text. +// Compile parses a regular expression and returns, if successful, +// a Regexp object that can be used to match against text. +// +// When matching against text, the regexp returns a match that +// begins as early as possible in the input (leftmost), and among those +// it chooses the one that a backtracking search would have found first. +// This so-called leftmost-first matching is the same semantics +// that Perl, Python, and other implementations use, although this +// package implements it without the expense of backtracking. +// For POSIX leftmost-longest matching, see CompilePOSIX. func Compile(expr string) (*Regexp, os.Error) { - re, err := syntax.Parse(expr, syntax.Perl) + return compile(expr, syntax.Perl, false) +} + +// CompilePOSIX is like Compile but restricts the regular expression +// to POSIX ERE (egrep) syntax and changes the match semantics to +// leftmost-longest. +// +// That is, when matching against text, the regexp returns a match that +// begins as early as possible in the input (leftmost), and among those +// it chooses a match that is as long as possible. +// This so-called leftmost-longest matching is the same semantics +// that early regular expression implementations used and that POSIX +// specifies. +// +// However, there can be multiple leftmost-longest matches, with different +// submatch choices, and here this package diverges from POSIX. +// Among the possible leftmost-longest matches, this package chooses +// the one that a backtracking search would have found first, while POSIX +// specifies that the match be chosen to maximize the length of the first +// subexpression, then the second, and so on from left to right. +// The POSIX rule is computationally prohibitive and not even well-defined. +// See http://swtch.com/~rsc/regexp/regexp2.html#posix for details. +func CompilePOSIX(expr string) (*Regexp, os.Error) { + return compile(expr, syntax.POSIX, true) +} + +func compile(expr string, mode syntax.Flags, longest bool) (*Regexp, os.Error) { + re, err := syntax.Parse(expr, mode) if err != nil { return nil, err } @@ -113,6 +150,8 @@ func Compile(expr string) (*Regexp, os.Error) { expr: expr, prog: prog, numSubexp: maxCap, + cond: prog.StartCond(), + longest: longest, } regexp.prefix, regexp.prefixComplete = prog.Prefix() if regexp.prefix != "" { @@ -121,7 +160,6 @@ func Compile(expr string) (*Regexp, os.Error) { regexp.prefixBytes = []byte(regexp.prefix) regexp.prefixRune, _ = utf8.DecodeRuneInString(regexp.prefix) } - regexp.cond = prog.StartCond() return regexp, nil } @@ -158,11 +196,29 @@ func (re *Regexp) put(z *machine) { func MustCompile(str string) *Regexp { regexp, error := Compile(str) if error != nil { - panic(`regexp: compiling "` + str + `": ` + error.String()) + panic(`regexp: Compile(` + quote(str) + `): ` + error.String()) } return regexp } +// MustCompilePOSIX is like CompilePOSIX but panics if the expression cannot be parsed. +// It simplifies safe initialization of global variables holding compiled regular +// expressions. +func MustCompilePOSIX(str string) *Regexp { + regexp, error := CompilePOSIX(str) + if error != nil { + panic(`regexp: CompilePOSIX(` + quote(str) + `): ` + error.String()) + } + return regexp +} + +func quote(s string) string { + if strconv.CanBackquote(s) { + return "`" + s + "`" + } + return strconv.Quote(s) +} + // NumSubexp returns the number of parenthesized subexpressions in this Regexp. func (re *Regexp) NumSubexp() int { return re.numSubexp diff --git a/src/pkg/exp/regexp/syntax/parse.go b/src/pkg/exp/regexp/syntax/parse.go index 954a0ad8a..1165db3e2 100644 --- a/src/pkg/exp/regexp/syntax/parse.go +++ b/src/pkg/exp/regexp/syntax/parse.go @@ -181,11 +181,29 @@ func (p *parser) maybeConcat(r int, flags Flags) bool { func (p *parser) newLiteral(r int, flags Flags) *Regexp { re := p.newRegexp(OpLiteral) re.Flags = flags + if flags&FoldCase != 0 { + r = minFoldRune(r) + } re.Rune0[0] = r re.Rune = re.Rune0[:1] return re } +// minFoldRune returns the minimum rune fold-equivalent to r. +func minFoldRune(r int) int { + if r < minFold || r > maxFold { + return r + } + min := r + r0 := r + for r = unicode.SimpleFold(r); r != r0; r = unicode.SimpleFold(r) { + if min > r { + min = r + } + } + return min +} + // literal pushes a literal regexp for the rune r on the stack // and returns that regexp. func (p *parser) literal(r int) { @@ -200,27 +218,32 @@ func (p *parser) op(op Op) *Regexp { return p.push(re) } -// repeat replaces the top stack element with itself repeated -// according to op. -func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (string, os.Error) { +// repeat replaces the top stack element with itself repeated according to op, min, max. +// before is the regexp suffix starting at the repetition operator. +// after is the regexp suffix following after the repetition operator. +// repeat returns an updated 'after' and an error, if any. +func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) (string, os.Error) { flags := p.flags if p.flags&PerlX != 0 { - if len(t) > 0 && t[0] == '?' { - t = t[1:] + if len(after) > 0 && after[0] == '?' { + after = after[1:] flags ^= NonGreedy } if lastRepeat != "" { // In Perl it is not allowed to stack repetition operators: // a** is a syntax error, not a doubled star, and a++ means // something else entirely, which we don't support! - return "", &Error{ErrInvalidRepeatOp, lastRepeat[:len(lastRepeat)-len(t)]} + return "", &Error{ErrInvalidRepeatOp, lastRepeat[:len(lastRepeat)-len(after)]} } } n := len(p.stack) if n == 0 { - return "", &Error{ErrMissingRepeatArgument, opstr} + return "", &Error{ErrMissingRepeatArgument, before[:len(before)-len(after)]} } sub := p.stack[n-1] + if sub.Op >= opPseudo { + return "", &Error{ErrMissingRepeatArgument, before[:len(before)-len(after)]} + } re := p.newRegexp(op) re.Min = min re.Max = max @@ -228,7 +251,7 @@ func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (strin re.Sub = re.Sub0[:1] re.Sub[0] = sub p.stack[n-1] = re - return t, nil + return after, nil } // concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation. @@ -704,6 +727,7 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) { return nil, err } case '*', '+', '?': + before := t switch t[0] { case '*': op = OpStar @@ -712,21 +736,31 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) { case '?': op = OpQuest } - if t, err = p.repeat(op, min, max, t[:1], t[1:], lastRepeat); err != nil { + after := t[1:] + if after, err = p.repeat(op, min, max, before, after, lastRepeat); err != nil { return nil, err } + repeat = before + t = after case '{': op = OpRepeat - min, max, tt, ok := p.parseRepeat(t) + before := t + min, max, after, ok := p.parseRepeat(t) if !ok { // If the repeat cannot be parsed, { is a literal. p.literal('{') t = t[1:] break } - if t, err = p.repeat(op, min, max, t[:len(t)-len(tt)], tt, lastRepeat); err != nil { + if min < 0 || min > 1000 || max > 1000 || max >= 0 && min > max { + // Numbers were too big, or max is present and min > max. + return nil, &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} + } + if after, err = p.repeat(op, min, max, before, after, lastRepeat); err != nil { return nil, err } + repeat = before + t = after case '\\': if p.flags&PerlX != 0 && len(t) >= 2 { switch t[1] { @@ -815,12 +849,14 @@ func Parse(s string, flags Flags) (*Regexp, os.Error) { // parseRepeat parses {min} (max=min) or {min,} (max=-1) or {min,max}. // If s is not of that form, it returns ok == false. +// If s has the right form but the values are too big, it returns min == -1, ok == true. func (p *parser) parseRepeat(s string) (min, max int, rest string, ok bool) { if s == "" || s[0] != '{' { return } s = s[1:] - if min, s, ok = p.parseInt(s); !ok { + var ok1 bool + if min, s, ok1 = p.parseInt(s); !ok1 { return } if s == "" { @@ -835,8 +871,11 @@ func (p *parser) parseRepeat(s string) (min, max int, rest string, ok bool) { } if s[0] == '}' { max = -1 - } else if max, s, ok = p.parseInt(s); !ok { + } else if max, s, ok1 = p.parseInt(s); !ok1 { return + } else if max < 0 { + // parseInt found too big a number + min = -1 } } if s == "" || s[0] != '}' { @@ -981,16 +1020,22 @@ func (p *parser) parseInt(s string) (n int, rest string, ok bool) { if len(s) >= 2 && s[0] == '0' && '0' <= s[1] && s[1] <= '9' { return } + t := s for s != "" && '0' <= s[0] && s[0] <= '9' { - // Avoid overflow. - if n >= 1e8 { - return - } - n = n*10 + int(s[0]) - '0' s = s[1:] } rest = s ok = true + // Have digits, compute value. + t = t[:len(t)-len(s)] + for i := 0; i < len(t); i++ { + // Avoid overflow. + if n >= 1e8 { + n = -1 + break + } + n = n*10 + int(t[i]) - '0' + } return } @@ -1125,6 +1170,8 @@ func (p *parser) parseRightParen() os.Error { if re2.Op != opLeftParen { return &Error{ErrMissingParen, p.wholeRegexp} } + // Restore flags at time of paren. + p.flags = re2.Flags if re2.Cap == 0 { // Just for grouping. p.push(re1) @@ -1330,9 +1377,18 @@ func (p *parser) appendGroup(r []int, g charGroup) []int { return r } +var anyTable = &unicode.RangeTable{ + []unicode.Range16{{0, 1<<16 - 1, 1}}, + []unicode.Range32{{1 << 16, unicode.MaxRune, 1}}, +} + // unicodeTable returns the unicode.RangeTable identified by name // and the table of additional fold-equivalent code points. func unicodeTable(name string) (*unicode.RangeTable, *unicode.RangeTable) { + // Special case: "Any" means any. + if name == "Any" { + return anyTable, anyTable + } if t := unicode.Categories[name]; t != nil { return t, unicode.FoldCategory[name] } diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go index a146c89c3..f20276c59 100644 --- a/src/pkg/exp/regexp/syntax/parse_test.go +++ b/src/pkg/exp/regexp/syntax/parse_test.go @@ -11,10 +11,12 @@ import ( "unicode" ) -var parseTests = []struct { +type parseTest struct { Regexp string Dump string -}{ +} + +var parseTests = []parseTest{ // Base cases {`a`, `lit{a}`}, {`a.`, `cat{lit{a}dot{}}`}, @@ -38,6 +40,12 @@ var parseTests = []struct { {`a{2}?`, `nrep{2,2 lit{a}}`}, {`a{2,3}?`, `nrep{2,3 lit{a}}`}, {`a{2,}?`, `nrep{2,-1 lit{a}}`}, + // Malformed { } are treated as literals. + {`x{1001`, `str{x{1001}`}, + {`x{9876543210`, `str{x{9876543210}`}, + {`x{9876543210,`, `str{x{9876543210,}`}, + {`x{2,1`, `str{x{2,1}`}, + {`x{1,9876543210`, `str{x{1,9876543210}`}, {``, `emp{}`}, {`|`, `emp{}`}, // alt{emp{}emp{}} but got factored {`|x|`, `alt{emp{}lit{x}emp{}}`}, @@ -101,6 +109,8 @@ var parseTests = []struct { {`\p{Lu}`, mkCharClass(unicode.IsUpper)}, {`[\p{Lu}]`, mkCharClass(unicode.IsUpper)}, {`(?i)[\p{Lu}]`, mkCharClass(isUpperFold)}, + {`\p{Any}`, `dot{}`}, + {`\p{^Any}`, `cc{}`}, // Hex, octal. {`[\012-\234]\141`, `cat{cc{0xa-0x9c}lit{a}}`}, @@ -174,14 +184,80 @@ var parseTests = []struct { {`(?-s).`, `dnl{}`}, {`(?:(?:^).)`, `cat{bol{}dot{}}`}, {`(?-s)(?:(?:^).)`, `cat{bol{}dnl{}}`}, + + // RE2 prefix_tests + {`abc|abd`, `cat{str{ab}cc{0x63-0x64}}`}, + {`a(?:b)c|abd`, `cat{str{ab}cc{0x63-0x64}}`}, + {`abc|abd|aef|bcx|bcy`, + `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}` + + `cat{str{bc}cc{0x78-0x79}}}`}, + {`abc|x|abd`, `alt{str{abc}lit{x}str{abd}}`}, + {`(?i)abc|ABD`, `cat{strfold{AB}cc{0x43-0x44 0x63-0x64}}`}, + {`[ab]c|[ab]d`, `cat{cc{0x61-0x62}cc{0x63-0x64}}`}, + {`(?:xx|yy)c|(?:xx|yy)d`, + `cat{alt{str{xx}str{yy}}cc{0x63-0x64}}`}, + {`x{2}|x{2}[0-9]`, + `cat{rep{2,2 lit{x}}alt{emp{}cc{0x30-0x39}}}`}, + {`x{2}y|x{2}[0-9]y`, + `cat{rep{2,2 lit{x}}alt{lit{y}cat{cc{0x30-0x39}lit{y}}}}`}, } const testFlags = MatchNL | PerlX | UnicodeGroups +func TestParseSimple(t *testing.T) { + testParseDump(t, parseTests, testFlags) +} + +var foldcaseTests = []parseTest{ + {`AbCdE`, `strfold{ABCDE}`}, + {`[Aa]`, `litfold{A}`}, + {`a`, `litfold{A}`}, + + // 0x17F is an old English long s (looks like an f) and folds to s. + // 0x212A is the Kelvin symbol and folds to k. + {`A[F-g]`, `cat{litfold{A}cc{0x41-0x7a 0x17f 0x212a}}`}, // [Aa][A-z...] + {`[[:upper:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, + {`[[:lower:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, +} + +func TestParseFoldCase(t *testing.T) { + testParseDump(t, foldcaseTests, FoldCase) +} + +var literalTests = []parseTest{ + {"(|)^$.[*+?]{5,10},\\", "str{(|)^$.[*+?]{5,10},\\}"}, +} + +func TestParseLiteral(t *testing.T) { + testParseDump(t, literalTests, Literal) +} + +var matchnlTests = []parseTest{ + {`.`, `dot{}`}, + {"\n", "lit{\n}"}, + {`[^a]`, `cc{0x0-0x60 0x62-0x10ffff}`}, + {`[a\n]`, `cc{0xa 0x61}`}, +} + +func TestParseMatchNL(t *testing.T) { + testParseDump(t, matchnlTests, MatchNL) +} + +var nomatchnlTests = []parseTest{ + {`.`, `dnl{}`}, + {"\n", "lit{\n}"}, + {`[^a]`, `cc{0x0-0x9 0xb-0x60 0x62-0x10ffff}`}, + {`[a\n]`, `cc{0xa 0x61}`}, +} + +func TestParseNoMatchNL(t *testing.T) { + testParseDump(t, nomatchnlTests, 0) +} + // Test Parse -> Dump. -func TestParseDump(t *testing.T) { - for _, tt := range parseTests { - re, err := Parse(tt.Regexp, testFlags) +func testParseDump(t *testing.T, tests []parseTest, flags Flags) { + for _, tt := range tests { + re, err := Parse(tt.Regexp, flags) if err != nil { t.Errorf("Parse(%#q): %v", tt.Regexp, err) continue @@ -360,3 +436,116 @@ func TestAppendRangeCollapse(t *testing.T) { t.Errorf("appendRange interlaced A-Z a-z = %s, want AZaz", string(r)) } } + +var invalidRegexps = []string{ + `(`, + `)`, + `(a`, + `(a|b|`, + `(a|b`, + `[a-z`, + `([a-z)`, + `x{1001}`, + `x{9876543210}`, + `x{2,1}`, + `x{1,9876543210}`, + "\xff", // Invalid UTF-8 + "[\xff]", + "[\\\xff]", + "\\\xff", + `(?P<name>a`, + `(?P<name>`, + `(?P<name`, + `(?P<x y>a)`, + `(?P<>a)`, + `[a-Z]`, + `(?i)[a-Z]`, + `a{100000}`, + `a{100000,}`, +} + +var onlyPerl = []string{ + `[a-b-c]`, + `\Qabc\E`, + `\Q*+?{[\E`, + `\Q\\E`, + `\Q\\\E`, + `\Q\\\\E`, + `\Q\\\\\E`, + `(?:a)`, + `(?P<name>a)`, +} + +var onlyPOSIX = []string{ + "a++", + "a**", + "a?*", + "a+*", + "a{1}*", + ".{1}{2}.{3}", +} + +func TestParseInvalidRegexps(t *testing.T) { + for _, regexp := range invalidRegexps { + if re, err := Parse(regexp, Perl); err == nil { + t.Errorf("Parse(%#q, Perl) = %s, should have failed", regexp, dump(re)) + } + if re, err := Parse(regexp, POSIX); err == nil { + t.Errorf("Parse(%#q, POSIX) = %s, should have failed", regexp, dump(re)) + } + } + for _, regexp := range onlyPerl { + if _, err := Parse(regexp, Perl); err != nil { + t.Errorf("Parse(%#q, Perl): %v", regexp, err) + } + if re, err := Parse(regexp, POSIX); err == nil { + t.Errorf("Parse(%#q, POSIX) = %s, should have failed", regexp, dump(re)) + } + } + for _, regexp := range onlyPOSIX { + if re, err := Parse(regexp, Perl); err == nil { + t.Errorf("Parse(%#q, Perl) = %s, should have failed", regexp, dump(re)) + } + if _, err := Parse(regexp, POSIX); err != nil { + t.Errorf("Parse(%#q, POSIX): %v", regexp, err) + } + } +} + +func TestToStringEquivalentParse(t *testing.T) { + for _, tt := range parseTests { + re, err := Parse(tt.Regexp, testFlags) + if err != nil { + t.Errorf("Parse(%#q): %v", tt.Regexp, err) + continue + } + d := dump(re) + if d != tt.Dump { + t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) + continue + } + + s := re.String() + if s != tt.Regexp { + // If ToString didn't return the original regexp, + // it must have found one with fewer parens. + // Unfortunately we can't check the length here, because + // ToString produces "\\{" for a literal brace, + // but "{" is a shorter equivalent in some contexts. + nre, err := Parse(s, testFlags) + if err != nil { + t.Errorf("Parse(%#q.String() = %#q): %v", tt.Regexp, t, err) + continue + } + nd := dump(nre) + if d != nd { + t.Errorf("Parse(%#q) -> %#q; %#q vs %#q", tt.Regexp, s, d, nd) + } + + ns := nre.String() + if s != ns { + t.Errorf("Parse(%#q) -> %#q -> %#q", tt.Regexp, s, ns) + } + } + } +} diff --git a/src/pkg/exp/regexp/syntax/prog.go b/src/pkg/exp/regexp/syntax/prog.go index d214d70b5..e92baaca0 100644 --- a/src/pkg/exp/regexp/syntax/prog.go +++ b/src/pkg/exp/regexp/syntax/prog.go @@ -57,7 +57,7 @@ func EmptyOpContext(r1, r2 int) EmptyOp { op |= EmptyBeginLine } if r2 < 0 { - op |= EmptyEndText + op |= EmptyEndText | EmptyEndLine } if r2 == '\n' { op |= EmptyEndLine diff --git a/src/pkg/exp/regexp/syntax/regexp.go b/src/pkg/exp/regexp/syntax/regexp.go index d8f51b903..033848df2 100644 --- a/src/pkg/exp/regexp/syntax/regexp.go +++ b/src/pkg/exp/regexp/syntax/regexp.go @@ -164,9 +164,9 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) { } b.WriteRune(']') case OpAnyCharNotNL: - b.WriteString(`[^\n]`) + b.WriteString(`(?-s:.)`) case OpAnyChar: - b.WriteRune('.') + b.WriteString(`(?s:.)`) case OpBeginLine: b.WriteRune('^') case OpEndLine: @@ -174,7 +174,11 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) { case OpBeginText: b.WriteString(`\A`) case OpEndText: - b.WriteString(`\z`) + if re.Flags&WasDollar != 0 { + b.WriteString(`(?-m:$)`) + } else { + b.WriteString(`\z`) + } case OpWordBoundary: b.WriteString(`\b`) case OpNoWordBoundary: @@ -192,7 +196,7 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) { } b.WriteRune(')') case OpStar, OpPlus, OpQuest, OpRepeat: - if sub := re.Sub[0]; sub.Op > OpCapture { + if sub := re.Sub[0]; sub.Op > OpCapture || sub.Op == OpLiteral && len(sub.Rune) > 1 { b.WriteString(`(?:`) writeRegexp(b, sub) b.WriteString(`)`) @@ -217,6 +221,9 @@ func writeRegexp(b *bytes.Buffer, re *Regexp) { } b.WriteRune('}') } + if re.Flags&NonGreedy != 0 { + b.WriteRune('?') + } case OpConcat: for _, sub := range re.Sub { if sub.Op == OpAlternate { diff --git a/src/pkg/exp/regexp/syntax/simplify_test.go b/src/pkg/exp/regexp/syntax/simplify_test.go index c8cec2183..879eff5be 100644 --- a/src/pkg/exp/regexp/syntax/simplify_test.go +++ b/src/pkg/exp/regexp/syntax/simplify_test.go @@ -18,7 +18,7 @@ var simplifyTests = []struct { {`(ab)*`, `(ab)*`}, {`(ab)+`, `(ab)+`}, {`(ab)?`, `(ab)?`}, - {`.`, `.`}, + {`.`, `(?s:.)`}, {`^`, `^`}, {`$`, `$`}, {`[ac]`, `[ac]`}, @@ -97,22 +97,22 @@ var simplifyTests = []struct { {`[^[:cntrl:][:^cntrl:]]`, `[^\x00-\x{10FFFF}]`}, // Full character classes - {`[[:cntrl:][:^cntrl:]]`, `.`}, + {`[[:cntrl:][:^cntrl:]]`, `(?s:.)`}, // Unicode case folding. {`(?i)A`, `(?i:A)`}, - {`(?i)a`, `(?i:a)`}, + {`(?i)a`, `(?i:A)`}, {`(?i)[A]`, `(?i:A)`}, {`(?i)[a]`, `(?i:A)`}, {`(?i)K`, `(?i:K)`}, - {`(?i)k`, `(?i:k)`}, - {`(?i)\x{212a}`, "(?i:\u212A)"}, + {`(?i)k`, `(?i:K)`}, + {`(?i)\x{212a}`, "(?i:K)"}, {`(?i)[K]`, "[Kk\u212A]"}, {`(?i)[k]`, "[Kk\u212A]"}, {`(?i)[\x{212a}]`, "[Kk\u212A]"}, {`(?i)[a-z]`, "[A-Za-z\u017F\u212A]"}, {`(?i)[\x00-\x{FFFD}]`, "[\\x00-\uFFFD]"}, - {`(?i)[\x00-\x{10FFFF}]`, `.`}, + {`(?i)[\x00-\x{10FFFF}]`, `(?s:.)`}, // Empty string as a regular expression. // The empty string must be preserved inside parens in order diff --git a/src/pkg/exp/regexp/testdata/README b/src/pkg/exp/regexp/testdata/README new file mode 100644 index 000000000..b1b301be8 --- /dev/null +++ b/src/pkg/exp/regexp/testdata/README @@ -0,0 +1,23 @@ +AT&T POSIX Test Files +See textregex.c for copyright + license. + +testregex.c http://www2.research.att.com/~gsf/testregex/testregex.c +basic.dat http://www2.research.att.com/~gsf/testregex/basic.dat +nullsubexpr.dat http://www2.research.att.com/~gsf/testregex/nullsubexpr.dat +repetition.dat http://www2.research.att.com/~gsf/testregex/repetition.dat + +The test data has been edited to reflect RE2/Go differences: + * In a star of a possibly empty match like (a*)* matching x, + the no match case runs the starred subexpression zero times, + not once. This is consistent with (a*)* matching a, which + runs the starred subexpression one time, not twice. + * The submatch choice is first match, not the POSIX rule. + +Such changes are marked with 'RE2/Go'. + + +RE2 Test Files + +re2-exhaustive.txt.bz2 and re2-search.txt are built by running +'make log' in the RE2 distribution. http://code.google.com/p/re2/. +The exhaustive file is compressed because it is huge. diff --git a/src/pkg/exp/regexp/testdata/basic.dat b/src/pkg/exp/regexp/testdata/basic.dat new file mode 100644 index 000000000..7859290ba --- /dev/null +++ b/src/pkg/exp/regexp/testdata/basic.dat @@ -0,0 +1,221 @@ +NOTE all standard compliant implementations should pass these : 2002-05-31 + +BE abracadabra$ abracadabracadabra (7,18) +BE a...b abababbb (2,7) +BE XXXXXX ..XXXXXX (2,8) +E \) () (1,2) +BE a] a]a (0,2) +B } } (0,1) +E \} } (0,1) +BE \] ] (0,1) +B ] ] (0,1) +E ] ] (0,1) +B { { (0,1) +B } } (0,1) +BE ^a ax (0,1) +BE \^a a^a (1,3) +BE a\^ a^ (0,2) +BE a$ aa (1,2) +BE a\$ a$ (0,2) +BE ^$ NULL (0,0) +E $^ NULL (0,0) +E a($) aa (1,2)(2,2) +E a*(^a) aa (0,1)(0,1) +E (..)*(...)* a (0,0) +E (..)*(...)* abcd (0,4)(2,4) +E (ab|a)(bc|c) abc (0,3)(0,2)(2,3) +E (ab)c|abc abc (0,3)(0,2) +E a{0}b ab (1,2) +E (a*)(b?)(b+)b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) +E (a*)(b{0,1})(b{1,})b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) +E a{9876543210} NULL BADBR +E ((a|a)|a) a (0,1)(0,1)(0,1) +E (a*)(a|aa) aaaa (0,4)(0,3)(3,4) +E a*(a.|aa) aaaa (0,4)(2,4) +E a(b)|c(d)|a(e)f aef (0,3)(?,?)(?,?)(1,2) +E (a|b)?.* b (0,1)(0,1) +E (a|b)c|a(b|c) ac (0,2)(0,1) +E (a|b)c|a(b|c) ab (0,2)(?,?)(1,2) +E (a|b)*c|(a|ab)*c abc (0,3)(1,2) +E (a|b)*c|(a|ab)*c xc (1,2) +E (.a|.b).*|.*(.a|.b) xa (0,2)(0,2) +E a?(ab|ba)ab abab (0,4)(0,2) +E a?(ac{0}b|ba)ab abab (0,4)(0,2) +E ab|abab abbabab (0,2) +E aba|bab|bba baaabbbaba (5,8) +E aba|bab baaabbbaba (6,9) +E (aa|aaa)*|(a|aaaaa) aa (0,2)(0,2) +E (a.|.a.)*|(a|.a...) aa (0,2)(0,2) +E ab|a xabc (1,3) +E ab|a xxabc (2,4) +Ei (Ab|cD)* aBcD (0,4)(2,4) +BE [^-] --a (2,3) +BE [a-]* --a (0,3) +BE [a-m-]* --amoma-- (0,4) +E :::1:::0:|:::1:1:0: :::0:::1:::1:::0: (8,17) +E :::1:::0:|:::1:1:1: :::0:::1:::1:::0: (8,17) +{E [[:upper:]] A (0,1) [[<element>]] not supported +E [[:lower:]]+ `az{ (1,3) +E [[:upper:]]+ @AZ[ (1,3) +# No collation in Go +#BE [[-]] [[-]] (2,4) +#BE [[.NIL.]] NULL ECOLLATE +#BE [[=aleph=]] NULL ECOLLATE +} +BE$ \n \n (0,1) +BEn$ \n \n (0,1) +BE$ [^a] \n (0,1) +BE$ \na \na (0,2) +E (a)(b)(c) abc (0,3)(0,1)(1,2)(2,3) +BE xxx xxx (0,3) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 6, (0,6) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) 2/7 (0,3) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 1,Feb 6 (5,11) +E3 ((((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))) x (0,1)(0,1)(0,1) +E3 ((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))* xx (0,2)(1,2)(1,2) +E a?(ab|ba)* ababababababababababababababababababababababababababababababababababababababababa (0,81)(79,81) +E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabbbbaa (18,25) +E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabaa (18,22) +E aaac|aabc|abac|abbc|baac|babc|bbac|bbbc baaabbbabac (7,11) +BE$ .* \x01\xff (0,2) +E aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa (53,57) +L aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa NOMATCH +E a*a*a*a*a*b aaaaaaaaab (0,10) +BE ^ NULL (0,0) +BE $ NULL (0,0) +BE ^$ NULL (0,0) +BE ^a$ a (0,1) +BE abc abc (0,3) +BE abc xabcy (1,4) +BE abc ababc (2,5) +BE ab*c abc (0,3) +BE ab*bc abc (0,3) +BE ab*bc abbc (0,4) +BE ab*bc abbbbc (0,6) +E ab+bc abbc (0,4) +E ab+bc abbbbc (0,6) +E ab?bc abbc (0,4) +E ab?bc abc (0,3) +E ab?c abc (0,3) +BE ^abc$ abc (0,3) +BE ^abc abcc (0,3) +BE abc$ aabc (1,4) +BE ^ abc (0,0) +BE $ abc (3,3) +BE a.c abc (0,3) +BE a.c axc (0,3) +BE a.*c axyzc (0,5) +BE a[bc]d abd (0,3) +BE a[b-d]e ace (0,3) +BE a[b-d] aac (1,3) +BE a[-b] a- (0,2) +BE a[b-] a- (0,2) +BE a] a] (0,2) +BE a[]]b a]b (0,3) +BE a[^bc]d aed (0,3) +BE a[^-b]c adc (0,3) +BE a[^]b]c adc (0,3) +E ab|cd abc (0,2) +E ab|cd abcd (0,2) +E a\(b a(b (0,3) +E a\(*b ab (0,2) +E a\(*b a((b (0,4) +E ((a)) abc (0,1)(0,1)(0,1) +E (a)b(c) abc (0,3)(0,1)(2,3) +E a+b+c aabbabc (4,7) +E a* aaa (0,3) +#E (a*)* - (0,0)(0,0) +E (a*)* - (0,0)(?,?) RE2/Go +E (a*)+ - (0,0)(0,0) +#E (a*|b)* - (0,0)(0,0) +E (a*|b)* - (0,0)(?,?) RE2/Go +E (a+|b)* ab (0,2)(1,2) +E (a+|b)+ ab (0,2)(1,2) +E (a+|b)? ab (0,1)(0,1) +BE [^ab]* cde (0,3) +#E (^)* - (0,0)(0,0) +E (^)* - (0,0)(?,?) RE2/Go +BE a* NULL (0,0) +E ([abc])*d abbbcd (0,6)(4,5) +E ([abc])*bcd abcd (0,4)(0,1) +E a|b|c|d|e e (0,1) +E (a|b|c|d|e)f ef (0,2)(0,1) +#E ((a*|b))* - (0,0)(0,0)(0,0) +E ((a*|b))* - (0,0)(?,?)(?,?) RE2/Go +BE abcd*efg abcdefg (0,7) +BE ab* xabyabbbz (1,3) +BE ab* xayabbbz (1,2) +E (ab|cd)e abcde (2,5)(2,4) +BE [abhgefdc]ij hij (0,3) +E (a|b)c*d abcd (1,4)(1,2) +E (ab|ab*)bc abc (0,3)(0,1) +E a([bc]*)c* abc (0,3)(1,3) +E a([bc]*)(c*d) abcd (0,4)(1,3)(3,4) +E a([bc]+)(c*d) abcd (0,4)(1,3)(3,4) +E a([bc]*)(c+d) abcd (0,4)(1,2)(2,4) +E a[bcd]*dcdcde adcdcde (0,7) +E (ab|a)b*c abc (0,3)(0,2) +E ((a)(b)c)(d) abcd (0,4)(0,3)(0,1)(1,2)(3,4) +BE [A-Za-z_][A-Za-z0-9_]* alpha (0,5) +E ^a(bc+|b[eh])g|.h$ abh (1,3) +E (bc+d$|ef*g.|h?i(j|k)) effgz (0,5)(0,5) +E (bc+d$|ef*g.|h?i(j|k)) ij (0,2)(0,2)(1,2) +E (bc+d$|ef*g.|h?i(j|k)) reffgz (1,6)(1,6) +E (((((((((a))))))))) a (0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1) +BE multiple words multiple words yeah (0,14) +E (.*)c(.*) abcde (0,5)(0,2)(3,5) +BE abcd abcd (0,4) +E a(bc)d abcd (0,4)(1,3) +E a[-]?c ac (0,3) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qaddafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mo'ammar Gadhafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Kaddafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qadhafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gadafi (0,14)(?,?)(10,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadafi (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moamar Gaddafi (0,14)(?,?)(9,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadhdhafi (0,18)(?,?)(13,15) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Khaddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafy (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghadafi (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muamar Kaddafi (0,14)(?,?)(9,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Quathafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gheddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Khadafy (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Qudhafi (0,15)(?,?)(10,12) +E a+(b|c)*d+ aabcdd (0,6)(3,4) +E ^.+$ vivi (0,4) +E ^(.+)$ vivi (0,4)(0,4) +E ^([^!.]+).att.com!(.+)$ gryphon.att.com!eby (0,19)(0,7)(16,19) +E ^([^!]+!)?([^!]+)$ bas (0,3)(?,?)(0,3) +E ^([^!]+!)?([^!]+)$ bar!bas (0,7)(0,4)(4,7) +E ^([^!]+!)?([^!]+)$ foo!bas (0,7)(0,4)(4,7) +E ^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(4,8)(8,11) +E ((foo)|(bar))!bas bar!bas (0,7)(0,3)(?,?)(0,3) +E ((foo)|(bar))!bas foo!bar!bas (4,11)(4,7)(?,?)(4,7) +E ((foo)|(bar))!bas foo!bas (0,7)(0,3)(0,3) +E ((foo)|bar)!bas bar!bas (0,7)(0,3) +E ((foo)|bar)!bas foo!bar!bas (4,11)(4,7) +E ((foo)|bar)!bas foo!bas (0,7)(0,3)(0,3) +E (foo|(bar))!bas bar!bas (0,7)(0,3)(0,3) +E (foo|(bar))!bas foo!bar!bas (4,11)(4,7)(4,7) +E (foo|(bar))!bas foo!bas (0,7)(0,3) +E (foo|bar)!bas bar!bas (0,7)(0,3) +E (foo|bar)!bas foo!bar!bas (4,11)(4,7) +E (foo|bar)!bas foo!bas (0,7)(0,3) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bas (0,3)(?,?)(0,3) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bar!bas (0,7)(0,4)(4,7) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(?,?)(?,?)(4,8)(8,11) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bas (0,7)(0,4)(4,7) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bas (0,3)(0,3)(?,?)(0,3) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bar!bas (0,7)(0,7)(0,4)(4,7) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bas (0,7)(0,7)(0,4)(4,7) +E .*(/XXX).* /XXX (0,4)(0,4) +E .*(\\XXX).* \XXX (0,4)(0,4) +E \\XXX \XXX (0,4) +E .*(/000).* /000 (0,4)(0,4) +E .*(\\000).* \000 (0,4)(0,4) +E \\000 \000 (0,4) diff --git a/src/pkg/exp/regexp/testdata/nullsubexpr.dat b/src/pkg/exp/regexp/testdata/nullsubexpr.dat new file mode 100644 index 000000000..2e18fbb91 --- /dev/null +++ b/src/pkg/exp/regexp/testdata/nullsubexpr.dat @@ -0,0 +1,79 @@ +NOTE null subexpression matches : 2002-06-06 + +E (a*)* a (0,1)(0,1) +#E SAME x (0,0)(0,0) +E SAME x (0,0)(?,?) RE2/Go +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) +E (a*)+ a (0,1)(0,1) +E SAME x (0,0)(0,0) +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) +E (a+)* a (0,1)(0,1) +E SAME x (0,0) +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) +E (a+)+ a (0,1)(0,1) +E SAME x NOMATCH +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) + +E ([a]*)* a (0,1)(0,1) +#E SAME x (0,0)(0,0) +E SAME x (0,0)(?,?) RE2/Go +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) +E ([a]*)+ a (0,1)(0,1) +E SAME x (0,0)(0,0) +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaax (0,6)(0,6) +E ([^b]*)* a (0,1)(0,1) +#E SAME b (0,0)(0,0) +E SAME b (0,0)(?,?) RE2/Go +E SAME aaaaaa (0,6)(0,6) +E SAME aaaaaab (0,6)(0,6) +E ([ab]*)* a (0,1)(0,1) +E SAME aaaaaa (0,6)(0,6) +E SAME ababab (0,6)(0,6) +E SAME bababa (0,6)(0,6) +E SAME b (0,1)(0,1) +E SAME bbbbbb (0,6)(0,6) +E SAME aaaabcde (0,5)(0,5) +E ([^a]*)* b (0,1)(0,1) +E SAME bbbbbb (0,6)(0,6) +#E SAME aaaaaa (0,0)(0,0) +E SAME aaaaaa (0,0)(?,?) RE2/Go +E ([^ab]*)* ccccxx (0,6)(0,6) +#E SAME ababab (0,0)(0,0) +E SAME ababab (0,0)(?,?) RE2/Go + +E ((z)+|a)* zabcde (0,2)(1,2) + +#{E a+? aaaaaa (0,1) no *? +? mimimal match ops +#E (a) aaa (0,1)(0,1) +#E (a*?) aaa (0,0)(0,0) +#E (a)*? aaa (0,0) +#E (a*?)*? aaa (0,0) +#} + +B \(a*\)*\(x\) x (0,1)(0,0)(0,1) +B \(a*\)*\(x\) ax (0,2)(0,1)(1,2) +B \(a*\)*\(x\) axa (0,2)(0,1)(1,2) +B \(a*\)*\(x\)\(\1\) x (0,1)(0,0)(0,1)(1,1) +B \(a*\)*\(x\)\(\1\) ax (0,2)(1,1)(1,2)(2,2) +B \(a*\)*\(x\)\(\1\) axa (0,3)(0,1)(1,2)(2,3) +B \(a*\)*\(x\)\(\1\)\(x\) axax (0,4)(0,1)(1,2)(2,3)(3,4) +B \(a*\)*\(x\)\(\1\)\(x\) axxa (0,3)(1,1)(1,2)(2,2)(2,3) + +#E (a*)*(x) x (0,1)(0,0)(0,1) +E (a*)*(x) x (0,1)(?,?)(0,1) RE2/Go +E (a*)*(x) ax (0,2)(0,1)(1,2) +E (a*)*(x) axa (0,2)(0,1)(1,2) + +E (a*)+(x) x (0,1)(0,0)(0,1) +E (a*)+(x) ax (0,2)(0,1)(1,2) +E (a*)+(x) axa (0,2)(0,1)(1,2) + +E (a*){2}(x) x (0,1)(0,0)(0,1) +E (a*){2}(x) ax (0,2)(1,1)(1,2) +E (a*){2}(x) axa (0,2)(1,1)(1,2) diff --git a/src/pkg/exp/regexp/testdata/re2-exhaustive.txt.bz2 b/src/pkg/exp/regexp/testdata/re2-exhaustive.txt.bz2 Binary files differnew file mode 100644 index 000000000..a357f2801 --- /dev/null +++ b/src/pkg/exp/regexp/testdata/re2-exhaustive.txt.bz2 diff --git a/src/pkg/exp/regexp/testdata/re2-search.txt b/src/pkg/exp/regexp/testdata/re2-search.txt new file mode 100644 index 000000000..f648e5527 --- /dev/null +++ b/src/pkg/exp/regexp/testdata/re2-search.txt @@ -0,0 +1,3667 @@ +# RE2 basic search tests built by make log +# Thu Sep 8 13:43:43 EDT 2011 +Regexp.SearchTests +strings +"" +"a" +regexps +"a" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:a)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"zyzzyva" +regexps +"a" +-;-;-;- +-;6-7;-;6-7 +"^(?:a)$" +-;-;-;- +-;-;-;- +"^(?:a)" +-;-;-;- +-;-;-;- +"(?:a)$" +-;-;-;- +-;6-7;-;6-7 +strings +"" +"aa" +regexps +"a+" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:a+)$" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:a+)" +-;-;-;- +0-2;0-2;0-2;0-2 +"(?:a+)$" +-;-;-;- +0-2;0-2;0-2;0-2 +strings +"" +"ab" +regexps +"(a+|b)+" +-;-;-;- +0-2 1-2;0-2 1-2;0-2 1-2;0-2 1-2 +"^(?:(a+|b)+)$" +-;-;-;- +0-2 1-2;0-2 1-2;0-2 1-2;0-2 1-2 +"^(?:(a+|b)+)" +-;-;-;- +0-2 1-2;0-2 1-2;0-2 1-2;0-2 1-2 +"(?:(a+|b)+)$" +-;-;-;- +0-2 1-2;0-2 1-2;0-2 1-2;0-2 1-2 +strings +"" +"xabcdx" +regexps +"ab|cd" +-;-;-;- +-;1-3;-;1-3 +"^(?:ab|cd)$" +-;-;-;- +-;-;-;- +"^(?:ab|cd)" +-;-;-;- +-;-;-;- +"(?:ab|cd)$" +-;-;-;- +-;-;-;- +strings +"" +"hello\ngoodbye\n" +regexps +"h.*od?" +-;-;-;- +-;0-5;-;0-5 +"^(?:h.*od?)$" +-;-;-;- +-;-;-;- +"^(?:h.*od?)" +-;-;-;- +-;0-5;-;0-5 +"(?:h.*od?)$" +-;-;-;- +-;-;-;- +strings +"" +"hello\ngoodbye\n" +regexps +"h.*o" +-;-;-;- +-;0-5;-;0-5 +"^(?:h.*o)$" +-;-;-;- +-;-;-;- +"^(?:h.*o)" +-;-;-;- +-;0-5;-;0-5 +"(?:h.*o)$" +-;-;-;- +-;-;-;- +strings +"" +"goodbye\nhello\n" +regexps +"h.*o" +-;-;-;- +-;8-13;-;8-13 +"^(?:h.*o)$" +-;-;-;- +-;-;-;- +"^(?:h.*o)" +-;-;-;- +-;-;-;- +"(?:h.*o)$" +-;-;-;- +-;-;-;- +strings +"" +"hello world" +regexps +"h.*o" +-;-;-;- +-;0-8;-;0-8 +"^(?:h.*o)$" +-;-;-;- +-;-;-;- +"^(?:h.*o)" +-;-;-;- +-;0-8;-;0-8 +"(?:h.*o)$" +-;-;-;- +-;-;-;- +strings +"" +"othello, world" +regexps +"h.*o" +-;-;-;- +-;2-11;-;2-11 +"^(?:h.*o)$" +-;-;-;- +-;-;-;- +"^(?:h.*o)" +-;-;-;- +-;-;-;- +"(?:h.*o)$" +-;-;-;- +-;-;-;- +strings +"" +"aaaaaaa" +regexps +"[^\\s\\S]" +-;-;-;- +-;-;-;- +"^(?:[^\\s\\S])$" +-;-;-;- +-;-;-;- +"^(?:[^\\s\\S])" +-;-;-;- +-;-;-;- +"(?:[^\\s\\S])$" +-;-;-;- +-;-;-;- +strings +"" +"aaaaaaa" +regexps +"a" +-;-;-;- +-;0-1;-;0-1 +"^(?:a)$" +-;-;-;- +-;-;-;- +"^(?:a)" +-;-;-;- +-;0-1;-;0-1 +"(?:a)$" +-;-;-;- +-;6-7;-;6-7 +strings +"" +"aaaaaaa" +regexps +"a*" +0-0;0-0;0-0;0-0 +0-7;0-7;0-7;0-7 +"^(?:a*)$" +0-0;0-0;0-0;0-0 +0-7;0-7;0-7;0-7 +"^(?:a*)" +0-0;0-0;0-0;0-0 +0-7;0-7;0-7;0-7 +"(?:a*)$" +0-0;0-0;0-0;0-0 +0-7;0-7;0-7;0-7 +strings +"" +"" +regexps +"a*" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:a*)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:a*)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:a*)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"" +regexps +"a*" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:a*)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:a*)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:a*)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"xabcdx" +regexps +"ab|cd" +-;-;-;- +-;1-3;-;1-3 +"^(?:ab|cd)$" +-;-;-;- +-;-;-;- +"^(?:ab|cd)" +-;-;-;- +-;-;-;- +"(?:ab|cd)$" +-;-;-;- +-;-;-;- +strings +"" +"cab" +regexps +"a" +-;-;-;- +-;1-2;-;1-2 +"^(?:a)$" +-;-;-;- +-;-;-;- +"^(?:a)" +-;-;-;- +-;-;-;- +"(?:a)$" +-;-;-;- +-;-;-;- +strings +"" +"cab" +regexps +"a*b" +-;-;-;- +-;1-3;-;1-3 +"^(?:a*b)$" +-;-;-;- +-;-;-;- +"^(?:a*b)" +-;-;-;- +-;-;-;- +"(?:a*b)$" +-;-;-;- +-;1-3;-;1-3 +strings +"" +"x" +regexps +"((((((((((((((((((((x))))))))))))))))))))" +-;-;-;- +0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 +"^(?:((((((((((((((((((((x)))))))))))))))))))))$" +-;-;-;- +0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 +"^(?:((((((((((((((((((((x)))))))))))))))))))))" +-;-;-;- +0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 +"(?:((((((((((((((((((((x)))))))))))))))))))))$" +-;-;-;- +0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1;0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 0-1 +strings +"" +"xxxabcdxxx" +regexps +"[abcd]" +-;-;-;- +-;3-4;-;3-4 +"^(?:[abcd])$" +-;-;-;- +-;-;-;- +"^(?:[abcd])" +-;-;-;- +-;-;-;- +"(?:[abcd])$" +-;-;-;- +-;-;-;- +strings +"" +"xxxabcdxxx" +regexps +"[^x]" +-;-;-;- +-;3-4;-;3-4 +"^(?:[^x])$" +-;-;-;- +-;-;-;- +"^(?:[^x])" +-;-;-;- +-;-;-;- +"(?:[^x])$" +-;-;-;- +-;-;-;- +strings +"" +"xxxabcdxxx" +regexps +"[abcd]+" +-;-;-;- +-;3-7;-;3-7 +"^(?:[abcd]+)$" +-;-;-;- +-;-;-;- +"^(?:[abcd]+)" +-;-;-;- +-;-;-;- +"(?:[abcd]+)$" +-;-;-;- +-;-;-;- +strings +"" +"xxxabcdxxx" +regexps +"[^x]+" +-;-;-;- +-;3-7;-;3-7 +"^(?:[^x]+)$" +-;-;-;- +-;-;-;- +"^(?:[^x]+)" +-;-;-;- +-;-;-;- +"(?:[^x]+)$" +-;-;-;- +-;-;-;- +strings +"" +"fo" +regexps +"(fo|foo)" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:(fo|foo))$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:(fo|foo))" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"(?:(fo|foo))$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +strings +"" +"foo" +regexps +"(foo|fo)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|fo))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|fo))" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:(foo|fo))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"aA" +regexps +"aa" +-;-;-;- +-;-;-;- +"^(?:aa)$" +-;-;-;- +-;-;-;- +"^(?:aa)" +-;-;-;- +-;-;-;- +"(?:aa)$" +-;-;-;- +-;-;-;- +strings +"" +"Aa" +regexps +"a" +-;-;-;- +-;1-2;-;1-2 +"^(?:a)$" +-;-;-;- +-;-;-;- +"^(?:a)" +-;-;-;- +-;-;-;- +"(?:a)$" +-;-;-;- +-;1-2;-;1-2 +strings +"" +"A" +regexps +"a" +-;-;-;- +-;-;-;- +"^(?:a)$" +-;-;-;- +-;-;-;- +"^(?:a)" +-;-;-;- +-;-;-;- +"(?:a)$" +-;-;-;- +-;-;-;- +strings +"" +"abc" +regexps +"ABC" +-;-;-;- +-;-;-;- +"^(?:ABC)$" +-;-;-;- +-;-;-;- +"^(?:ABC)" +-;-;-;- +-;-;-;- +"(?:ABC)$" +-;-;-;- +-;-;-;- +strings +"" +"XABCY" +regexps +"abc" +-;-;-;- +-;-;-;- +"^(?:abc)$" +-;-;-;- +-;-;-;- +"^(?:abc)" +-;-;-;- +-;-;-;- +"(?:abc)$" +-;-;-;- +-;-;-;- +strings +"" +"xabcy" +regexps +"ABC" +-;-;-;- +-;-;-;- +"^(?:ABC)$" +-;-;-;- +-;-;-;- +"^(?:ABC)" +-;-;-;- +-;-;-;- +"(?:ABC)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"foo|bar|[A-Z]" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:foo|bar|[A-Z])$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:foo|bar|[A-Z])" +-;-;-;- +0-3;0-3;0-3;0-3 +"(?:foo|bar|[A-Z])$" +-;-;-;- +0-3;0-3;0-3;0-3 +strings +"" +"foo" +regexps +"^(foo|bar|[A-Z])" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z]))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z]))" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^(foo|bar|[A-Z]))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"foo\n" +regexps +"(foo|bar|[A-Z])$" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])$)" +-;-;-;- +-;-;-;- +"(?:(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"(foo|bar|[A-Z])$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|bar|[A-Z])$)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"foo\n" +regexps +"^(foo|bar|[A-Z])$" +-;-;-;- +-;-;-;- +"^(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +"^(?:^(foo|bar|[A-Z])$)" +-;-;-;- +-;-;-;- +"(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"^(foo|bar|[A-Z])$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z])$)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"bar" +regexps +"^(foo|bar|[A-Z])$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(foo|bar|[A-Z])$)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"X" +regexps +"^(foo|bar|[A-Z])$" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"^(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"^(?:^(foo|bar|[A-Z])$)" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +strings +"" +"XY" +regexps +"^(foo|bar|[A-Z])$" +-;-;-;- +-;-;-;- +"^(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +"^(?:^(foo|bar|[A-Z])$)" +-;-;-;- +-;-;-;- +"(?:^(foo|bar|[A-Z])$)$" +-;-;-;- +-;-;-;- +strings +"" +"fo" +regexps +"^(fo|foo)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^(fo|foo)$)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^(fo|foo)$)" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"(?:^(fo|foo)$)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +strings +"" +"foo" +regexps +"^(fo|foo)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(fo|foo)$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^(fo|foo)$)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^(fo|foo)$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"fo" +regexps +"^^(fo|foo)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^^(fo|foo)$)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^^(fo|foo)$)" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"(?:^^(fo|foo)$)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +strings +"" +"foo" +regexps +"^^(fo|foo)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^^(fo|foo)$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^^(fo|foo)$)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^^(fo|foo)$)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"" +regexps +"^$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"^^$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"" +regexps +"^$$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"^$$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"^^$$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^$$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^^$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^^$$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^$$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^^$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"^^^^^^^^$$$$$$$$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^^^^^^^$$$$$$$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^^^^^^^$$$$$$$$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^^^^^^^^$$$$$$$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"^(?:^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^)" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"(?:^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"$" +0-0;0-0;0-0;0-0 +-;1-1;-;1-1 +"^(?:$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:$)$" +0-0;0-0;0-0;0-0 +-;1-1;-;1-1 +strings +"" +"nofoo foo that" +regexps +"\\bfoo\\b" +-;-;-;- +-;6-9;-;6-9 +"^(?:\\bfoo\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bfoo\\b)" +-;-;-;- +-;-;-;- +"(?:\\bfoo\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"faoa x" +regexps +"a\\b" +-;-;-;- +-;3-4;-;3-4 +"^(?:a\\b)$" +-;-;-;- +-;-;-;- +"^(?:a\\b)" +-;-;-;- +-;-;-;- +"(?:a\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"bar x" +regexps +"\\bbar" +-;-;-;- +-;0-3;-;0-3 +"^(?:\\bbar)$" +-;-;-;- +-;-;-;- +"^(?:\\bbar)" +-;-;-;- +-;0-3;-;0-3 +"(?:\\bbar)$" +-;-;-;- +-;-;-;- +strings +"" +"foo\nbar x" +regexps +"\\bbar" +-;-;-;- +-;4-7;-;4-7 +"^(?:\\bbar)$" +-;-;-;- +-;-;-;- +"^(?:\\bbar)" +-;-;-;- +-;-;-;- +"(?:\\bbar)$" +-;-;-;- +-;-;-;- +strings +"" +"foobar" +regexps +"bar\\b" +-;-;-;- +-;3-6;-;3-6 +"^(?:bar\\b)$" +-;-;-;- +-;-;-;- +"^(?:bar\\b)" +-;-;-;- +-;-;-;- +"(?:bar\\b)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"foobar\nxxx" +regexps +"bar\\b" +-;-;-;- +-;3-6;-;3-6 +"^(?:bar\\b)$" +-;-;-;- +-;-;-;- +"^(?:bar\\b)" +-;-;-;- +-;-;-;- +"(?:bar\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"(foo|bar|[A-Z])\\b" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(foo|bar|[A-Z])\\b)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"foo\n" +regexps +"(foo|bar|[A-Z])\\b" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"^(?:(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])\\b)" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"(?:(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"\\b" +-;-;-;- +-;-;-;- +"^(?:\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b)" +-;-;-;- +-;-;-;- +"(?:\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"\\b" +-;-;-;- +-;0-0;-;0-0 +"^(?:\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b)" +-;-;-;- +-;0-0;-;0-0 +"(?:\\b)$" +-;-;-;- +-;1-1;-;1-1 +strings +"" +"foo" +regexps +"\\b(foo|bar|[A-Z])" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z]))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z]))" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:\\b(foo|bar|[A-Z]))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"X" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-1 0-1;0-1 0-1;0-1 0-1;0-1 0-1 +strings +"" +"XY" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +-;-;-;- +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +-;-;-;- +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"bar" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"foo" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"foo\n" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"ffoo bbar N x" +regexps +"\\b(foo|bar|[A-Z])\\b" +-;-;-;- +-;10-11 10-11;-;10-11 10-11 +"^(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b(foo|bar|[A-Z])\\b)" +-;-;-;- +-;-;-;- +"(?:\\b(foo|bar|[A-Z])\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"fo" +regexps +"\\b(fo|foo)\\b" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:\\b(fo|foo)\\b)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:\\b(fo|foo)\\b)" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"(?:\\b(fo|foo)\\b)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +strings +"" +"foo" +regexps +"\\b(fo|foo)\\b" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(fo|foo)\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:\\b(fo|foo)\\b)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:\\b(fo|foo)\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"" +regexps +"\\b\\b" +-;-;-;- +-;-;-;- +"^(?:\\b\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b\\b)" +-;-;-;- +-;-;-;- +"(?:\\b\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"\\b\\b" +-;-;-;- +-;0-0;-;0-0 +"^(?:\\b\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\b\\b)" +-;-;-;- +-;0-0;-;0-0 +"(?:\\b\\b)$" +-;-;-;- +-;1-1;-;1-1 +strings +"" +"" +regexps +"\\b$" +-;-;-;- +-;-;-;- +"^(?:\\b$)$" +-;-;-;- +-;-;-;- +"^(?:\\b$)" +-;-;-;- +-;-;-;- +"(?:\\b$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"\\b$" +-;-;-;- +-;1-1;-;1-1 +"^(?:\\b$)$" +-;-;-;- +-;-;-;- +"^(?:\\b$)" +-;-;-;- +-;-;-;- +"(?:\\b$)$" +-;-;-;- +-;1-1;-;1-1 +strings +"" +"y x" +regexps +"\\b$" +-;-;-;- +-;3-3;-;3-3 +"^(?:\\b$)$" +-;-;-;- +-;-;-;- +"^(?:\\b$)" +-;-;-;- +-;-;-;- +"(?:\\b$)$" +-;-;-;- +-;3-3;-;3-3 +strings +"" +"x" +regexps +"\\b.$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\b.$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\b.$)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\b.$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"fo" +regexps +"^\\b(fo|foo)\\b" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^\\b(fo|foo)\\b)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"^(?:^\\b(fo|foo)\\b)" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +"(?:^\\b(fo|foo)\\b)$" +-;-;-;- +0-2 0-2;0-2 0-2;0-2 0-2;0-2 0-2 +strings +"" +"foo" +regexps +"^\\b(fo|foo)\\b" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^\\b(fo|foo)\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:^\\b(fo|foo)\\b)" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"(?:^\\b(fo|foo)\\b)$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"" +regexps +"^\\b" +-;-;-;- +-;-;-;- +"^(?:^\\b)$" +-;-;-;- +-;-;-;- +"^(?:^\\b)" +-;-;-;- +-;-;-;- +"(?:^\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^\\b" +-;-;-;- +-;0-0;-;0-0 +"^(?:^\\b)$" +-;-;-;- +-;-;-;- +"^(?:^\\b)" +-;-;-;- +-;0-0;-;0-0 +"(?:^\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"^\\b\\b" +-;-;-;- +-;-;-;- +"^(?:^\\b\\b)$" +-;-;-;- +-;-;-;- +"^(?:^\\b\\b)" +-;-;-;- +-;-;-;- +"(?:^\\b\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^\\b\\b" +-;-;-;- +-;0-0;-;0-0 +"^(?:^\\b\\b)$" +-;-;-;- +-;-;-;- +"^(?:^\\b\\b)" +-;-;-;- +-;0-0;-;0-0 +"(?:^\\b\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"^\\b$" +-;-;-;- +-;-;-;- +"^(?:^\\b$)$" +-;-;-;- +-;-;-;- +"^(?:^\\b$)" +-;-;-;- +-;-;-;- +"(?:^\\b$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^\\b$" +-;-;-;- +-;-;-;- +"^(?:^\\b$)$" +-;-;-;- +-;-;-;- +"^(?:^\\b$)" +-;-;-;- +-;-;-;- +"(?:^\\b$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^\\b.$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^\\b.$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^\\b.$)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:^\\b.$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"x" +regexps +"^\\b.\\b$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^\\b.\\b$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^\\b.\\b$)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:^\\b.\\b$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"" +regexps +"^^^^^^^^\\b$$$$$$$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\b$$$$$$$)$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\b$$$$$$$)" +-;-;-;- +-;-;-;- +"(?:^^^^^^^^\\b$$$$$$$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^^^^^^^^\\b.$$$$$$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^^^^^^^^\\b.$$$$$$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:^^^^^^^^\\b.$$$$$$)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:^^^^^^^^\\b.$$$$$$)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"x" +regexps +"^^^^^^^^\\b$$$$$$$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\b$$$$$$$)$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\b$$$$$$$)" +-;-;-;- +-;-;-;- +"(?:^^^^^^^^\\b$$$$$$$)$" +-;-;-;- +-;-;-;- +strings +"" +"n foo xfoox that" +regexps +"\\Bfoo\\B" +-;-;-;- +-;7-10;-;7-10 +"^(?:\\Bfoo\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\Bfoo\\B)" +-;-;-;- +-;-;-;- +"(?:\\Bfoo\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"faoa x" +regexps +"a\\B" +-;-;-;- +-;1-2;-;1-2 +"^(?:a\\B)$" +-;-;-;- +-;-;-;- +"^(?:a\\B)" +-;-;-;- +-;-;-;- +"(?:a\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"bar x" +regexps +"\\Bbar" +-;-;-;- +-;-;-;- +"^(?:\\Bbar)$" +-;-;-;- +-;-;-;- +"^(?:\\Bbar)" +-;-;-;- +-;-;-;- +"(?:\\Bbar)$" +-;-;-;- +-;-;-;- +strings +"" +"foo\nbar x" +regexps +"\\Bbar" +-;-;-;- +-;-;-;- +"^(?:\\Bbar)$" +-;-;-;- +-;-;-;- +"^(?:\\Bbar)" +-;-;-;- +-;-;-;- +"(?:\\Bbar)$" +-;-;-;- +-;-;-;- +strings +"" +"foobar" +regexps +"bar\\B" +-;-;-;- +-;-;-;- +"^(?:bar\\B)$" +-;-;-;- +-;-;-;- +"^(?:bar\\B)" +-;-;-;- +-;-;-;- +"(?:bar\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foobar\nxxx" +regexps +"bar\\B" +-;-;-;- +-;-;-;- +"^(?:bar\\B)$" +-;-;-;- +-;-;-;- +"^(?:bar\\B)" +-;-;-;- +-;-;-;- +"(?:bar\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foox" +regexps +"(foo|bar|[A-Z])\\B" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"^(?:(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])\\B)" +-;-;-;- +-;0-3 0-3;-;0-3 0-3 +"(?:(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foo\n" +regexps +"(foo|bar|[A-Z])\\B" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"\\B" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"\\B" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"foo" +regexps +"\\B(foo|bar|[A-Z])" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z]))$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z]))" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z]))$" +-;-;-;- +-;-;-;- +strings +"" +"xXy" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;1-2 1-2;-;1-2 1-2 +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"XY" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"XYZ" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;1-2 1-2;-;1-2 1-2 +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"abara" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;1-4 1-4;-;1-4 1-4 +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"xfoo_" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;1-4 1-4;-;1-4 1-4 +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"xfoo\n" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foo bar vNx" +regexps +"\\B(foo|bar|[A-Z])\\B" +-;-;-;- +-;9-10 9-10;-;9-10 9-10 +"^(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|bar|[A-Z])\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|bar|[A-Z])\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"xfoo" +regexps +"\\B(fo|foo)\\B" +-;-;-;- +-;1-3 1-3;-;1-3 1-3 +"^(?:\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(fo|foo)\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"xfooo" +regexps +"\\B(foo|fo)\\B" +-;-;-;- +-;1-4 1-4;-;1-4 1-4 +"^(?:\\B(foo|fo)\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(foo|fo)\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(foo|fo)\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"\\B\\B" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B\\B)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:\\B\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"\\B\\B" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B\\B)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:\\B\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"\\B$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:\\B$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:\\B$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"\\B$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"y x" +regexps +"\\B$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:\\B$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"\\B.$" +-;-;-;- +-;-;-;- +"^(?:\\B.$)$" +-;-;-;- +-;-;-;- +"^(?:\\B.$)" +-;-;-;- +-;-;-;- +"(?:\\B.$)$" +-;-;-;- +-;-;-;- +strings +"" +"fo" +regexps +"^\\B(fo|foo)\\B" +-;-;-;- +-;-;-;- +"^(?:^\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +"^(?:^\\B(fo|foo)\\B)" +-;-;-;- +-;-;-;- +"(?:^\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"^\\B(fo|foo)\\B" +-;-;-;- +-;-;-;- +"^(?:^\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +"^(?:^\\B(fo|foo)\\B)" +-;-;-;- +-;-;-;- +"(?:^\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"^\\B" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^\\B" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"^\\B\\B" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B\\B)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^\\B\\B)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^\\B\\B" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B\\B)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^\\B\\B)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"" +regexps +"^\\B$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^\\B$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^\\B$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^\\B$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^\\B$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^\\B$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"^\\B.$" +-;-;-;- +-;-;-;- +"^(?:^\\B.$)$" +-;-;-;- +-;-;-;- +"^(?:^\\B.$)" +-;-;-;- +-;-;-;- +"(?:^\\B.$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^\\B.\\B$" +-;-;-;- +-;-;-;- +"^(?:^\\B.\\B$)$" +-;-;-;- +-;-;-;- +"^(?:^\\B.\\B$)" +-;-;-;- +-;-;-;- +"(?:^\\B.\\B$)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"^^^^^^^^\\B$$$$$$$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^^^^^^^\\B$$$$$$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^^^^^^^^\\B$$$$$$$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^^^^^^^^\\B$$$$$$$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^^^^^^^^\\B.$$$$$$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\B.$$$$$$)$" +-;-;-;- +-;-;-;- +"^(?:^^^^^^^^\\B.$$$$$$)" +-;-;-;- +-;-;-;- +"(?:^^^^^^^^\\B.$$$$$$)$" +-;-;-;- +-;-;-;- +strings +"" +"x" +regexps +"^^^^^^^^\\B$$$$$$$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^^^^^^^\\B$$$$$$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^^^^^^^\\B$$$$$$$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^^^^^^^^\\B$$$$$$$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"\\bx\\b" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\bx\\b)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\bx\\b)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\bx\\b)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"x>" +regexps +"\\bx\\b" +-;-;-;- +-;0-1;-;0-1 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;0-1;-;0-1 +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"<x" +regexps +"\\bx\\b" +-;-;-;- +-;1-2;-;1-2 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;1-2;-;1-2 +strings +"" +"<x>" +regexps +"\\bx\\b" +-;-;-;- +-;1-2;-;1-2 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"ax" +regexps +"\\bx\\b" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"xb" +regexps +"\\bx\\b" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"axb" +regexps +"\\bx\\b" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"«x" +regexps +"\\bx\\b" +-;-;-;- +-;2-3;-;2-3 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;2-3;-;2-3 +strings +"" +"x»" +regexps +"\\bx\\b" +-;-;-;- +-;0-1;-;0-1 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;0-1;-;0-1 +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"«x»" +regexps +"\\bx\\b" +-;-;-;- +-;2-3;-;2-3 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"axb" +regexps +"\\bx\\b" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"áxβ" +regexps +"\\bx\\b" +-;-;-;- +-;2-3;-;2-3 +"^(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +"^(?:\\bx\\b)" +-;-;-;- +-;-;-;- +"(?:\\bx\\b)$" +-;-;-;- +-;-;-;- +strings +"" +"axb" +regexps +"\\Bx\\B" +-;-;-;- +-;1-2;-;1-2 +"^(?:\\Bx\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\Bx\\B)" +-;-;-;- +-;-;-;- +"(?:\\Bx\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"áxβ" +regexps +"\\Bx\\B" +-;-;-;- +-;-;-;- +"^(?:\\Bx\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\Bx\\B)" +-;-;-;- +-;-;-;- +"(?:\\Bx\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"" +regexps +"^$^$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$^$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^$^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"" +regexps +"^$^" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$^)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:^$^)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:^$^)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"" +regexps +"$^$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:$^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"^(?:$^$)" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +"(?:$^$)$" +0-0;0-0;0-0;0-0 +0-0;0-0;0-0;0-0 +strings +"" +"x" +regexps +"^$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"^$^" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x" +regexps +"$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\ny" +regexps +"^$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\ny" +regexps +"^$^" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\ny" +regexps +"$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\n\ny" +regexps +"^$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\n\ny" +regexps +"^$^" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^$^)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:^$^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"x\n\ny" +regexps +"$^$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:$^$)" +0-0;0-0;0-0;0-0 +-;-;-;- +"(?:$^$)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"foo$bar" +regexps +"^(foo\\$)$" +-;-;-;- +-;-;-;- +"^(?:^(foo\\$)$)$" +-;-;-;- +-;-;-;- +"^(?:^(foo\\$)$)" +-;-;-;- +-;-;-;- +"(?:^(foo\\$)$)$" +-;-;-;- +-;-;-;- +strings +"" +"foo$bar" +regexps +"(foo\\$)" +-;-;-;- +-;0-4 0-4;-;0-4 0-4 +"^(?:(foo\\$))$" +-;-;-;- +-;-;-;- +"^(?:(foo\\$))" +-;-;-;- +-;0-4 0-4;-;0-4 0-4 +"(?:(foo\\$))$" +-;-;-;- +-;-;-;- +strings +"" +"abc" +regexps +"^...$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^...$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^...$)" +-;-;-;- +0-3;0-3;0-3;0-3 +"(?:^...$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +strings +"" +"本" +regexps +"^本$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^本$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^本$)" +-;-;-;- +0-3;0-3;0-3;0-3 +"(?:^本$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +strings +"" +"日本語" +regexps +"^...$" +-;-;-;- +0-9;0-9;0-9;0-9 +"^(?:^...$)$" +-;-;-;- +0-9;0-9;0-9;0-9 +"^(?:^...$)" +-;-;-;- +0-9;0-9;0-9;0-9 +"(?:^...$)$" +-;-;-;- +0-9;0-9;0-9;0-9 +strings +"" +".本." +regexps +"^...$" +-;-;-;- +0-5;0-5;0-5;0-5 +"^(?:^...$)$" +-;-;-;- +0-5;0-5;0-5;0-5 +"^(?:^...$)" +-;-;-;- +0-5;0-5;0-5;0-5 +"(?:^...$)$" +-;-;-;- +0-5;0-5;0-5;0-5 +strings +"" +"本" +regexps +"^\\C\\C\\C$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^\\C\\C\\C$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +"^(?:^\\C\\C\\C$)" +-;-;-;- +0-3;0-3;0-3;0-3 +"(?:^\\C\\C\\C$)$" +-;-;-;- +0-3;0-3;0-3;0-3 +strings +"" +"本" +regexps +"^\\C$" +-;-;-;- +-;-;-;- +"^(?:^\\C$)$" +-;-;-;- +-;-;-;- +"^(?:^\\C$)" +-;-;-;- +-;-;-;- +"(?:^\\C$)$" +-;-;-;- +-;-;-;- +strings +"" +"日本語" +regexps +"^\\C\\C\\C$" +-;-;-;- +-;-;-;- +"^(?:^\\C\\C\\C$)$" +-;-;-;- +-;-;-;- +"^(?:^\\C\\C\\C$)" +-;-;-;- +-;-;-;- +"(?:^\\C\\C\\C$)$" +-;-;-;- +-;-;-;- +strings +"" +"日本語" +regexps +"^...$" +-;-;-;- +0-9;0-9;0-9;0-9 +"^(?:^...$)$" +-;-;-;- +0-9;0-9;0-9;0-9 +"^(?:^...$)" +-;-;-;- +0-9;0-9;0-9;0-9 +"(?:^...$)$" +-;-;-;- +0-9;0-9;0-9;0-9 +strings +"" +"日本語" +regexps +"^.........$" +-;-;-;- +-;-;-;- +"^(?:^.........$)$" +-;-;-;- +-;-;-;- +"^(?:^.........$)" +-;-;-;- +-;-;-;- +"(?:^.........$)$" +-;-;-;- +-;-;-;- +strings +"" +".本." +regexps +"^...$" +-;-;-;- +0-5;0-5;0-5;0-5 +"^(?:^...$)$" +-;-;-;- +0-5;0-5;0-5;0-5 +"^(?:^...$)" +-;-;-;- +0-5;0-5;0-5;0-5 +"(?:^...$)$" +-;-;-;- +0-5;0-5;0-5;0-5 +strings +"" +".本." +regexps +"^.....$" +-;-;-;- +-;-;-;- +"^(?:^.....$)$" +-;-;-;- +-;-;-;- +"^(?:^.....$)" +-;-;-;- +-;-;-;- +"(?:^.....$)$" +-;-;-;- +-;-;-;- +strings +"" +"xfooo" +regexps +"\\B(fo|foo)\\B" +-;-;-;- +-;1-3 1-3;-;1-4 1-4 +"^(?:\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +"^(?:\\B(fo|foo)\\B)" +-;-;-;- +-;-;-;- +"(?:\\B(fo|foo)\\B)$" +-;-;-;- +-;-;-;- +strings +"" +"foo" +regexps +"(fo|foo)" +-;-;-;- +0-3 0-3;0-2 0-2;0-3 0-3;0-3 0-3 +"^(?:(fo|foo))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +"^(?:(fo|foo))" +-;-;-;- +0-3 0-3;0-2 0-2;0-3 0-3;0-3 0-3 +"(?:(fo|foo))$" +-;-;-;- +0-3 0-3;0-3 0-3;0-3 0-3;0-3 0-3 +strings +"" +"a" +regexps +"\\141" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\141)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\141)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\141)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"0" +regexps +"\\060" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\060)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\060)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\060)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"00" +regexps +"\\0600" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\0600)$" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\0600)" +-;-;-;- +0-2;0-2;0-2;0-2 +"(?:\\0600)$" +-;-;-;- +0-2;0-2;0-2;0-2 +strings +"" +"08" +regexps +"\\608" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\608)$" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\608)" +-;-;-;- +0-2;0-2;0-2;0-2 +"(?:\\608)$" +-;-;-;- +0-2;0-2;0-2;0-2 +strings +"" +"" +regexps +"\\01" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\01)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\01)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\01)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"8" +regexps +"\\018" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\018)$" +-;-;-;- +0-2;0-2;0-2;0-2 +"^(?:\\018)" +-;-;-;- +0-2;0-2;0-2;0-2 +"(?:\\018)$" +-;-;-;- +0-2;0-2;0-2;0-2 +strings +"" +"a" +regexps +"\\x{61}" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x{61})$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x{61})" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\x{61})$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"a" +regexps +"\\x61" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x61)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x61)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\x61)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"a" +regexps +"\\x{00000061}" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x{00000061})$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:\\x{00000061})" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:\\x{00000061})$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"aαβb" +regexps +"\\p{Greek}+" +-;-;-;- +-;1-5;-;1-5 +"^(?:\\p{Greek}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{Greek}+)" +-;-;-;- +-;-;-;- +"(?:\\p{Greek}+)$" +-;-;-;- +-;-;-;- +strings +"" +"aαβb" +regexps +"\\P{Greek}+" +-;-;-;- +-;0-1;-;0-1 +"^(?:\\P{Greek}+)$" +-;-;-;- +-;-;-;- +"^(?:\\P{Greek}+)" +-;-;-;- +-;0-1;-;0-1 +"(?:\\P{Greek}+)$" +-;-;-;- +-;5-6;-;5-6 +strings +"" +"aαβb" +regexps +"\\p{^Greek}+" +-;-;-;- +-;0-1;-;0-1 +"^(?:\\p{^Greek}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{^Greek}+)" +-;-;-;- +-;0-1;-;0-1 +"(?:\\p{^Greek}+)$" +-;-;-;- +-;5-6;-;5-6 +strings +"" +"aαβb" +regexps +"\\P{^Greek}+" +-;-;-;- +-;1-5;-;1-5 +"^(?:\\P{^Greek}+)$" +-;-;-;- +-;-;-;- +"^(?:\\P{^Greek}+)" +-;-;-;- +-;-;-;- +"(?:\\P{^Greek}+)$" +-;-;-;- +-;-;-;- +strings +"" +"abc123" +regexps +"[^0-9]+" +-;-;-;- +-;0-3;-;0-3 +"^(?:[^0-9]+)$" +-;-;-;- +-;-;-;- +"^(?:[^0-9]+)" +-;-;-;- +-;0-3;-;0-3 +"(?:[^0-9]+)$" +-;-;-;- +-;-;-;- +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\p{Nd}+" +-;-;-;- +-;3-6;-;3-6 +"^(?:\\p{Nd}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{Nd}+)" +-;-;-;- +-;-;-;- +"(?:\\p{Nd}+)$" +-;-;-;- +-;-;-;- +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\p{^Nd}+" +-;-;-;- +-;0-3;-;0-3 +"^(?:\\p{^Nd}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{^Nd}+)" +-;-;-;- +-;0-3;-;0-3 +"(?:\\p{^Nd}+)$" +-;-;-;- +-;6-22;-;6-22 +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\P{Nd}+" +-;-;-;- +-;0-3;-;0-3 +"^(?:\\P{Nd}+)$" +-;-;-;- +-;-;-;- +"^(?:\\P{Nd}+)" +-;-;-;- +-;0-3;-;0-3 +"(?:\\P{Nd}+)$" +-;-;-;- +-;6-22;-;6-22 +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\P{^Nd}+" +-;-;-;- +-;3-6;-;3-6 +"^(?:\\P{^Nd}+)$" +-;-;-;- +-;-;-;- +"^(?:\\P{^Nd}+)" +-;-;-;- +-;-;-;- +"(?:\\P{^Nd}+)$" +-;-;-;- +-;-;-;- +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\pN+" +-;-;-;- +-;3-22;-;3-22 +"^(?:\\pN+)$" +-;-;-;- +-;-;-;- +"^(?:\\pN+)" +-;-;-;- +-;-;-;- +"(?:\\pN+)$" +-;-;-;- +-;3-22;-;3-22 +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\p{N}+" +-;-;-;- +-;3-22;-;3-22 +"^(?:\\p{N}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{N}+)" +-;-;-;- +-;-;-;- +"(?:\\p{N}+)$" +-;-;-;- +-;3-22;-;3-22 +strings +"" +"abc123²³¼½¾₀₉" +regexps +"\\p{^N}+" +-;-;-;- +-;0-3;-;0-3 +"^(?:\\p{^N}+)$" +-;-;-;- +-;-;-;- +"^(?:\\p{^N}+)" +-;-;-;- +-;0-3;-;0-3 +"(?:\\p{^N}+)$" +-;-;-;- +-;-;-;- +strings +"" +"abc123" +regexps +"\\p{Any}+" +-;-;-;- +0-6;0-6;0-6;0-6 +"^(?:\\p{Any}+)$" +-;-;-;- +0-6;0-6;0-6;0-6 +"^(?:\\p{Any}+)" +-;-;-;- +0-6;0-6;0-6;0-6 +"(?:\\p{Any}+)$" +-;-;-;- +0-6;0-6;0-6;0-6 +strings +"" +"@AaB" +regexps +"(?i)[@-A]+" +-;-;-;- +-;0-3;-;0-3 +"^(?:(?i)[@-A]+)$" +-;-;-;- +-;-;-;- +"^(?:(?i)[@-A]+)" +-;-;-;- +-;0-3;-;0-3 +"(?:(?i)[@-A]+)$" +-;-;-;- +-;-;-;- +strings +"" +"aAzZ" +regexps +"(?i)[A-Z]+" +-;-;-;- +0-4;0-4;0-4;0-4 +"^(?:(?i)[A-Z]+)$" +-;-;-;- +0-4;0-4;0-4;0-4 +"^(?:(?i)[A-Z]+)" +-;-;-;- +0-4;0-4;0-4;0-4 +"(?:(?i)[A-Z]+)$" +-;-;-;- +0-4;0-4;0-4;0-4 +strings +"" +"Aa\\" +regexps +"(?i)[^\\\\]+" +-;-;-;- +-;0-2;-;0-2 +"^(?:(?i)[^\\\\]+)$" +-;-;-;- +-;-;-;- +"^(?:(?i)[^\\\\]+)" +-;-;-;- +-;0-2;-;0-2 +"(?:(?i)[^\\\\]+)$" +-;-;-;- +-;-;-;- +strings +"" +"acegikmoqsuwyACEGIKMOQSUWY" +regexps +"(?i)[acegikmoqsuwy]+" +-;-;-;- +0-26;0-26;0-26;0-26 +"^(?:(?i)[acegikmoqsuwy]+)$" +-;-;-;- +0-26;0-26;0-26;0-26 +"^(?:(?i)[acegikmoqsuwy]+)" +-;-;-;- +0-26;0-26;0-26;0-26 +"(?:(?i)[acegikmoqsuwy]+)$" +-;-;-;- +0-26;0-26;0-26;0-26 +strings +"" +"@AaB" +regexps +"[@-A]+" +-;-;-;- +-;0-2;-;0-2 +"^(?:[@-A]+)$" +-;-;-;- +-;-;-;- +"^(?:[@-A]+)" +-;-;-;- +-;0-2;-;0-2 +"(?:[@-A]+)$" +-;-;-;- +-;-;-;- +strings +"" +"aAzZ" +regexps +"[A-Z]+" +-;-;-;- +-;1-2;-;1-2 +"^(?:[A-Z]+)$" +-;-;-;- +-;-;-;- +"^(?:[A-Z]+)" +-;-;-;- +-;-;-;- +"(?:[A-Z]+)$" +-;-;-;- +-;3-4;-;3-4 +strings +"" +"Aa\\" +regexps +"[^\\\\]+" +-;-;-;- +-;0-2;-;0-2 +"^(?:[^\\\\]+)$" +-;-;-;- +-;-;-;- +"^(?:[^\\\\]+)" +-;-;-;- +-;0-2;-;0-2 +"(?:[^\\\\]+)$" +-;-;-;- +-;-;-;- +strings +"" +"acegikmoqsuwyACEGIKMOQSUWY" +regexps +"[acegikmoqsuwy]+" +-;-;-;- +-;0-13;-;0-13 +"^(?:[acegikmoqsuwy]+)$" +-;-;-;- +-;-;-;- +"^(?:[acegikmoqsuwy]+)" +-;-;-;- +-;0-13;-;0-13 +"(?:[acegikmoqsuwy]+)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"^abc" +-;-;-;- +-;0-3;-;0-3 +"^(?:^abc)$" +-;-;-;- +-;-;-;- +"^(?:^abc)" +-;-;-;- +-;0-3;-;0-3 +"(?:^abc)$" +-;-;-;- +-;-;-;- +strings +"" +"aabcdef" +regexps +"^abc" +-;-;-;- +-;-;-;- +"^(?:^abc)$" +-;-;-;- +-;-;-;- +"^(?:^abc)" +-;-;-;- +-;-;-;- +"(?:^abc)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"^[ay]*[bx]+c" +-;-;-;- +-;0-3;-;0-3 +"^(?:^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +"^(?:^[ay]*[bx]+c)" +-;-;-;- +-;0-3;-;0-3 +"(?:^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +strings +"" +"aabcdef" +regexps +"^[ay]*[bx]+c" +-;-;-;- +-;0-4;-;0-4 +"^(?:^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +"^(?:^[ay]*[bx]+c)" +-;-;-;- +-;0-4;-;0-4 +"(?:^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"def$" +-;-;-;- +-;3-6;-;3-6 +"^(?:def$)$" +-;-;-;- +-;-;-;- +"^(?:def$)" +-;-;-;- +-;-;-;- +"(?:def$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"def$" +-;-;-;- +-;-;-;- +"^(?:def$)$" +-;-;-;- +-;-;-;- +"^(?:def$)" +-;-;-;- +-;-;-;- +"(?:def$)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"d[ex][fy]$" +-;-;-;- +-;3-6;-;3-6 +"^(?:d[ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:d[ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:d[ex][fy]$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"d[ex][fy]$" +-;-;-;- +-;-;-;- +"^(?:d[ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:d[ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:d[ex][fy]$)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"[dz][ex][fy]$" +-;-;-;- +-;3-6;-;3-6 +"^(?:[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:[dz][ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:[dz][ex][fy]$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"[dz][ex][fy]$" +-;-;-;- +-;-;-;- +"^(?:[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:[dz][ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"(?m)^abc" +-;-;-;- +-;0-3;-;0-3 +"^(?:(?m)^abc)$" +-;-;-;- +-;-;-;- +"^(?:(?m)^abc)" +-;-;-;- +-;0-3;-;0-3 +"(?:(?m)^abc)$" +-;-;-;- +-;-;-;- +strings +"" +"aabcdef" +regexps +"(?m)^abc" +-;-;-;- +-;-;-;- +"^(?:(?m)^abc)$" +-;-;-;- +-;-;-;- +"^(?:(?m)^abc)" +-;-;-;- +-;-;-;- +"(?:(?m)^abc)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"(?m)^[ay]*[bx]+c" +-;-;-;- +-;0-3;-;0-3 +"^(?:(?m)^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +"^(?:(?m)^[ay]*[bx]+c)" +-;-;-;- +-;0-3;-;0-3 +"(?:(?m)^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +strings +"" +"aabcdef" +regexps +"(?m)^[ay]*[bx]+c" +-;-;-;- +-;0-4;-;0-4 +"^(?:(?m)^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +"^(?:(?m)^[ay]*[bx]+c)" +-;-;-;- +-;0-4;-;0-4 +"(?:(?m)^[ay]*[bx]+c)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"(?m)def$" +-;-;-;- +-;3-6;-;3-6 +"^(?:(?m)def$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)def$)" +-;-;-;- +-;-;-;- +"(?:(?m)def$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"(?m)def$" +-;-;-;- +-;-;-;- +"^(?:(?m)def$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)def$)" +-;-;-;- +-;-;-;- +"(?:(?m)def$)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"(?m)d[ex][fy]$" +-;-;-;- +-;3-6;-;3-6 +"^(?:(?m)d[ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)d[ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:(?m)d[ex][fy]$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"(?m)d[ex][fy]$" +-;-;-;- +-;-;-;- +"^(?:(?m)d[ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)d[ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:(?m)d[ex][fy]$)$" +-;-;-;- +-;-;-;- +strings +"" +"abcdef" +regexps +"(?m)[dz][ex][fy]$" +-;-;-;- +-;3-6;-;3-6 +"^(?:(?m)[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)[dz][ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:(?m)[dz][ex][fy]$)$" +-;-;-;- +-;3-6;-;3-6 +strings +"" +"abcdeff" +regexps +"(?m)[dz][ex][fy]$" +-;-;-;- +-;-;-;- +"^(?:(?m)[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +"^(?:(?m)[dz][ex][fy]$)" +-;-;-;- +-;-;-;- +"(?:(?m)[dz][ex][fy]$)$" +-;-;-;- +-;-;-;- +strings +"" +"a" +regexps +"^" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"^(?:^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^)" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"(?:^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"a" +regexps +"^^" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"^(?:^^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +"^(?:^^)" +0-0;0-0;0-0;0-0 +-;0-0;-;0-0 +"(?:^^)$" +0-0;0-0;0-0;0-0 +-;-;-;- +strings +"" +"a" +regexps +"a" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:a)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"a" +regexps +"ab*" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:ab*)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:ab*)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:ab*)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"a" +regexps +"a\\C*" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a\\C*)$" +-;-;-;- +0-1;0-1;0-1;0-1 +"^(?:a\\C*)" +-;-;-;- +0-1;0-1;0-1;0-1 +"(?:a\\C*)$" +-;-;-;- +0-1;0-1;0-1;0-1 +strings +"" +"baba" +regexps +"a\\C*|ba\\C" +-;-;-;- +-;0-3;-;0-3 +"^(?:a\\C*|ba\\C)$" +-;-;-;- +-;-;-;- +"^(?:a\\C*|ba\\C)" +-;-;-;- +-;0-3;-;0-3 +"(?:a\\C*|ba\\C)$" +-;-;-;- +-;1-4;-;1-4 diff --git a/src/pkg/exp/regexp/testdata/repetition.dat b/src/pkg/exp/regexp/testdata/repetition.dat new file mode 100644 index 000000000..e6361f51a --- /dev/null +++ b/src/pkg/exp/regexp/testdata/repetition.dat @@ -0,0 +1,163 @@ +NOTE implicit vs. explicit repetitions : 2009-02-02 + +# Glenn Fowler <gsf@research.att.com> +# conforming matches (column 4) must match one of the following BREs +# NOMATCH +# (0,.)\((\(.\),\(.\))(?,?)(\2,\3)\)* +# (0,.)\((\(.\),\(.\))(\2,\3)(?,?)\)* +# i.e., each 3-tuple has two identical elements and one (?,?) + +E ((..)|(.)) NULL NOMATCH +E ((..)|(.))((..)|(.)) NULL NOMATCH +E ((..)|(.))((..)|(.))((..)|(.)) NULL NOMATCH + +E ((..)|(.)){1} NULL NOMATCH +E ((..)|(.)){2} NULL NOMATCH +E ((..)|(.)){3} NULL NOMATCH + +E ((..)|(.))* NULL (0,0) + +E ((..)|(.)) a (0,1)(0,1)(?,?)(0,1) +E ((..)|(.))((..)|(.)) a NOMATCH +E ((..)|(.))((..)|(.))((..)|(.)) a NOMATCH + +E ((..)|(.)){1} a (0,1)(0,1)(?,?)(0,1) +E ((..)|(.)){2} a NOMATCH +E ((..)|(.)){3} a NOMATCH + +E ((..)|(.))* a (0,1)(0,1)(?,?)(0,1) + +E ((..)|(.)) aa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.))((..)|(.)) aa (0,2)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2) +E ((..)|(.))((..)|(.))((..)|(.)) aa NOMATCH + +E ((..)|(.)){1} aa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.)){2} aa (0,2)(1,2)(?,?)(1,2) +E ((..)|(.)){3} aa NOMATCH + +E ((..)|(.))* aa (0,2)(0,2)(0,2)(?,?) + +E ((..)|(.)) aaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.))((..)|(.)) aaa (0,3)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3) +E ((..)|(.))((..)|(.))((..)|(.)) aaa (0,3)(0,1)(?,?)(0,1)(1,2)(?,?)(1,2)(2,3)(?,?)(2,3) + +E ((..)|(.)){1} aaa (0,2)(0,2)(0,2)(?,?) +#E ((..)|(.)){2} aaa (0,3)(2,3)(?,?)(2,3) +E ((..)|(.)){2} aaa (0,3)(2,3)(0,2)(2,3) RE2/Go +E ((..)|(.)){3} aaa (0,3)(2,3)(?,?)(2,3) + +#E ((..)|(.))* aaa (0,3)(2,3)(?,?)(2,3) +E ((..)|(.))* aaa (0,3)(2,3)(0,2)(2,3) RE2/Go + +E ((..)|(.)) aaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.))((..)|(.)) aaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) +E ((..)|(.))((..)|(.))((..)|(.)) aaaa (0,4)(0,2)(0,2)(?,?)(2,3)(?,?)(2,3)(3,4)(?,?)(3,4) + +E ((..)|(.)){1} aaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.)){2} aaaa (0,4)(2,4)(2,4)(?,?) +#E ((..)|(.)){3} aaaa (0,4)(3,4)(?,?)(3,4) +E ((..)|(.)){3} aaaa (0,4)(3,4)(0,2)(3,4) RE2/Go + +E ((..)|(.))* aaaa (0,4)(2,4)(2,4)(?,?) + +E ((..)|(.)) aaaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.))((..)|(.)) aaaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) +E ((..)|(.))((..)|(.))((..)|(.)) aaaaa (0,5)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,5)(?,?)(4,5) + +E ((..)|(.)){1} aaaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.)){2} aaaaa (0,4)(2,4)(2,4)(?,?) +#E ((..)|(.)){3} aaaaa (0,5)(4,5)(?,?)(4,5) +E ((..)|(.)){3} aaaaa (0,5)(4,5)(2,4)(4,5) RE2/Go + +#E ((..)|(.))* aaaaa (0,5)(4,5)(?,?)(4,5) +E ((..)|(.))* aaaaa (0,5)(4,5)(2,4)(4,5) RE2/Go + +E ((..)|(.)) aaaaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.))((..)|(.)) aaaaaa (0,4)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?) +E ((..)|(.))((..)|(.))((..)|(.)) aaaaaa (0,6)(0,2)(0,2)(?,?)(2,4)(2,4)(?,?)(4,6)(4,6)(?,?) + +E ((..)|(.)){1} aaaaaa (0,2)(0,2)(0,2)(?,?) +E ((..)|(.)){2} aaaaaa (0,4)(2,4)(2,4)(?,?) +E ((..)|(.)){3} aaaaaa (0,6)(4,6)(4,6)(?,?) + +E ((..)|(.))* aaaaaa (0,6)(4,6)(4,6)(?,?) + +NOTE additional repetition tests graciously provided by Chris Kuklewicz www.haskell.org 2009-02-02 + +# These test a bug in OS X / FreeBSD / NetBSD, and libtree. +# Linux/GLIBC gets the {8,} and {8,8} wrong. + +:HA#100:E X(.?){0,}Y X1234567Y (0,9)(7,8) +:HA#101:E X(.?){1,}Y X1234567Y (0,9)(7,8) +:HA#102:E X(.?){2,}Y X1234567Y (0,9)(7,8) +:HA#103:E X(.?){3,}Y X1234567Y (0,9)(7,8) +:HA#104:E X(.?){4,}Y X1234567Y (0,9)(7,8) +:HA#105:E X(.?){5,}Y X1234567Y (0,9)(7,8) +:HA#106:E X(.?){6,}Y X1234567Y (0,9)(7,8) +:HA#107:E X(.?){7,}Y X1234567Y (0,9)(7,8) +:HA#108:E X(.?){8,}Y X1234567Y (0,9)(8,8) +#:HA#110:E X(.?){0,8}Y X1234567Y (0,9)(7,8) +:HA#110:E X(.?){0,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#111:E X(.?){1,8}Y X1234567Y (0,9)(7,8) +:HA#111:E X(.?){1,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#112:E X(.?){2,8}Y X1234567Y (0,9)(7,8) +:HA#112:E X(.?){2,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#113:E X(.?){3,8}Y X1234567Y (0,9)(7,8) +:HA#113:E X(.?){3,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#114:E X(.?){4,8}Y X1234567Y (0,9)(7,8) +:HA#114:E X(.?){4,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#115:E X(.?){5,8}Y X1234567Y (0,9)(7,8) +:HA#115:E X(.?){5,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#116:E X(.?){6,8}Y X1234567Y (0,9)(7,8) +:HA#116:E X(.?){6,8}Y X1234567Y (0,9)(8,8) RE2/Go +#:HA#117:E X(.?){7,8}Y X1234567Y (0,9)(7,8) +:HA#117:E X(.?){7,8}Y X1234567Y (0,9)(8,8) RE2/Go +:HA#118:E X(.?){8,8}Y X1234567Y (0,9)(8,8) + +# These test a fixed bug in my regex-tdfa that did not keep the expanded +# form properly grouped, so right association did the wrong thing with +# these ambiguous patterns (crafted just to test my code when I became +# suspicious of my implementation). The first subexpression should use +# "ab" then "a" then "bcd". + +# OS X / FreeBSD / NetBSD badly fail many of these, with impossible +# results like (0,6)(4,5)(6,6). + +:HA#260:E (a|ab|c|bcd){0,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#261:E (a|ab|c|bcd){1,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#262:E (a|ab|c|bcd){2,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#263:E (a|ab|c|bcd){3,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#264:E (a|ab|c|bcd){4,}(d*) ababcd NOMATCH +:HA#265:E (a|ab|c|bcd){0,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#266:E (a|ab|c|bcd){1,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#267:E (a|ab|c|bcd){2,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#268:E (a|ab|c|bcd){3,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#269:E (a|ab|c|bcd){4,10}(d*) ababcd NOMATCH +:HA#270:E (a|ab|c|bcd)*(d*) ababcd (0,6)(3,6)(6,6) +:HA#271:E (a|ab|c|bcd)+(d*) ababcd (0,6)(3,6)(6,6) + +# The above worked on Linux/GLIBC but the following often fail. +# They also trip up OS X / FreeBSD / NetBSD: + +#:HA#280:E (ab|a|c|bcd){0,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#280:E (ab|a|c|bcd){0,}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#281:E (ab|a|c|bcd){1,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#281:E (ab|a|c|bcd){1,}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#282:E (ab|a|c|bcd){2,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#282:E (ab|a|c|bcd){2,}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#283:E (ab|a|c|bcd){3,}(d*) ababcd (0,6)(3,6)(6,6) +:HA#283:E (ab|a|c|bcd){3,}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +:HA#284:E (ab|a|c|bcd){4,}(d*) ababcd NOMATCH +#:HA#285:E (ab|a|c|bcd){0,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#285:E (ab|a|c|bcd){0,10}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#286:E (ab|a|c|bcd){1,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#286:E (ab|a|c|bcd){1,10}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#287:E (ab|a|c|bcd){2,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#287:E (ab|a|c|bcd){2,10}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#288:E (ab|a|c|bcd){3,10}(d*) ababcd (0,6)(3,6)(6,6) +:HA#288:E (ab|a|c|bcd){3,10}(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +:HA#289:E (ab|a|c|bcd){4,10}(d*) ababcd NOMATCH +#:HA#290:E (ab|a|c|bcd)*(d*) ababcd (0,6)(3,6)(6,6) +:HA#290:E (ab|a|c|bcd)*(d*) ababcd (0,6)(4,5)(5,6) RE2/Go +#:HA#291:E (ab|a|c|bcd)+(d*) ababcd (0,6)(3,6)(6,6) +:HA#291:E (ab|a|c|bcd)+(d*) ababcd (0,6)(4,5)(5,6) RE2/Go diff --git a/src/pkg/exp/regexp/testdata/testregex.c b/src/pkg/exp/regexp/testdata/testregex.c new file mode 100644 index 000000000..37545d057 --- /dev/null +++ b/src/pkg/exp/regexp/testdata/testregex.c @@ -0,0 +1,2286 @@ +#pragma prototyped noticed + +/* + * regex(3) test harness + * + * build: cc -o testregex testregex.c + * help: testregex --man + * note: REG_* features are detected by #ifdef; if REG_* are enums + * then supply #define REG_foo REG_foo for each enum REG_foo + * + * Glenn Fowler <gsf@research.att.com> + * AT&T Research + * + * PLEASE: publish your tests so everyone can benefit + * + * The following license covers testregex.c and all associated test data. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following disclaimer: + * + * THIS SOFTWARE IS PROVIDED BY AT&T ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL AT&T BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2010-06-10 $\0\n"; + +#if _PACKAGE_ast +#include <ast.h> +#else +#include <sys/types.h> +#endif + +#include <stdio.h> +#include <regex.h> +#include <ctype.h> +#include <setjmp.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdlib.h> +#include <locale.h> +#endif + +#ifndef RE_DUP_MAX +#define RE_DUP_MAX 32767 +#endif + +#if !_PACKAGE_ast +#undef REG_DISCIPLINE +#endif + +#ifndef REG_DELIMITED +#undef _REG_subcomp +#endif + +#define TEST_ARE 0x00000001 +#define TEST_BRE 0x00000002 +#define TEST_ERE 0x00000004 +#define TEST_KRE 0x00000008 +#define TEST_LRE 0x00000010 +#define TEST_SRE 0x00000020 + +#define TEST_EXPAND 0x00000100 +#define TEST_LENIENT 0x00000200 + +#define TEST_QUERY 0x00000400 +#define TEST_SUB 0x00000800 +#define TEST_UNSPECIFIED 0x00001000 +#define TEST_VERIFY 0x00002000 +#define TEST_AND 0x00004000 +#define TEST_OR 0x00008000 + +#define TEST_DELIMIT 0x00010000 +#define TEST_OK 0x00020000 +#define TEST_SAME 0x00040000 + +#define TEST_ACTUAL 0x00100000 +#define TEST_BASELINE 0x00200000 +#define TEST_FAIL 0x00400000 +#define TEST_PASS 0x00800000 +#define TEST_SUMMARY 0x01000000 + +#define TEST_IGNORE_ERROR 0x02000000 +#define TEST_IGNORE_OVER 0x04000000 +#define TEST_IGNORE_POSITION 0x08000000 + +#define TEST_CATCH 0x10000000 +#define TEST_VERBOSE 0x20000000 + +#define TEST_DECOMP 0x40000000 + +#define TEST_GLOBAL (TEST_ACTUAL|TEST_AND|TEST_BASELINE|TEST_CATCH|TEST_FAIL|TEST_IGNORE_ERROR|TEST_IGNORE_OVER|TEST_IGNORE_POSITION|TEST_OR|TEST_PASS|TEST_SUMMARY|TEST_VERBOSE) + +#ifdef REG_DISCIPLINE + + +#include <stk.h> + +typedef struct Disc_s +{ + regdisc_t disc; + int ordinal; + Sfio_t* sp; +} Disc_t; + +static void* +compf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc) +{ + Disc_t* dp = (Disc_t*)disc; + + return (void*)((char*)0 + ++dp->ordinal); +} + +static int +execf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc) +{ + Disc_t* dp = (Disc_t*)disc; + + sfprintf(dp->sp, "{%-.*s}(%lu:%d)", xlen, xstr, (char*)data - (char*)0, slen); + return atoi(xstr); +} + +static void* +resizef(void* handle, void* data, size_t size) +{ + if (!size) + return 0; + return stkalloc((Sfio_t*)handle, size); +} + +#endif + +#ifndef NiL +#ifdef __STDC__ +#define NiL 0 +#else +#define NiL (char*)0 +#endif +#endif + +#define H(x) do{if(html)fprintf(stderr,x);}while(0) +#define T(x) fprintf(stderr,x) + +static void +help(int html) +{ +H("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"); +H("<HTML>\n"); +H("<HEAD>\n"); +H("<TITLE>testregex man document</TITLE>\n"); +H("</HEAD>\n"); +H("<BODY bgcolor=white>\n"); +H("<PRE>\n"); +T("NAME\n"); +T(" testregex - regex(3) test harness\n"); +T("\n"); +T("SYNOPSIS\n"); +T(" testregex [ options ]\n"); +T("\n"); +T("DESCRIPTION\n"); +T(" testregex reads regex(3) test specifications, one per line, from the\n"); +T(" standard input and writes one output line for each failed test. A\n"); +T(" summary line is written after all tests are done. Each successful\n"); +T(" test is run again with REG_NOSUB. Unsupported features are noted\n"); +T(" before the first test, and tests requiring these features are\n"); +T(" silently ignored.\n"); +T("\n"); +T("OPTIONS\n"); +T(" -c catch signals and non-terminating calls\n"); +T(" -e ignore error return mismatches\n"); +T(" -h list help on standard error\n"); +T(" -n do not repeat successful tests with regnexec()\n"); +T(" -o ignore match[] overrun errors\n"); +T(" -p ignore negative position mismatches\n"); +T(" -s use stack instead of malloc\n"); +T(" -x do not repeat successful tests with REG_NOSUB\n"); +T(" -v list each test line\n"); +T(" -A list failed test lines with actual answers\n"); +T(" -B list all test lines with actual answers\n"); +T(" -F list failed test lines\n"); +T(" -P list passed test lines\n"); +T(" -S output one summary line\n"); +T("\n"); +T("INPUT FORMAT\n"); +T(" Input lines may be blank, a comment beginning with #, or a test\n"); +T(" specification. A specification is five fields separated by one\n"); +T(" or more tabs. NULL denotes the empty string and NIL denotes the\n"); +T(" 0 pointer.\n"); +T("\n"); +T(" Field 1: the regex(3) flags to apply, one character per REG_feature\n"); +T(" flag. The test is skipped if REG_feature is not supported by the\n"); +T(" implementation. If the first character is not [BEASKLP] then the\n"); +T(" specification is a global control line. One or more of [BEASKLP] may be\n"); +T(" specified; the test will be repeated for each mode.\n"); +T("\n"); +T(" B basic BRE (grep, ed, sed)\n"); +T(" E REG_EXTENDED ERE (egrep)\n"); +T(" A REG_AUGMENTED ARE (egrep with negation)\n"); +T(" S REG_SHELL SRE (sh glob)\n"); +T(" K REG_SHELL|REG_AUGMENTED KRE (ksh glob)\n"); +T(" L REG_LITERAL LRE (fgrep)\n"); +T("\n"); +T(" a REG_LEFT|REG_RIGHT implicit ^...$\n"); +T(" b REG_NOTBOL lhs does not match ^\n"); +T(" c REG_COMMENT ignore space and #...\\n\n"); +T(" d REG_SHELL_DOT explicit leading . match\n"); +T(" e REG_NOTEOL rhs does not match $\n"); +T(" f REG_MULTIPLE multiple \\n separated patterns\n"); +T(" g FNM_LEADING_DIR testfnmatch only -- match until /\n"); +T(" h REG_MULTIREF multiple digit backref\n"); +T(" i REG_ICASE ignore case\n"); +T(" j REG_SPAN . matches \\n\n"); +T(" k REG_ESCAPE \\ to ecape [...] delimiter\n"); +T(" l REG_LEFT implicit ^...\n"); +T(" m REG_MINIMAL minimal match\n"); +T(" n REG_NEWLINE explicit \\n match\n"); +T(" o REG_ENCLOSED (|&) magic inside [@|&](...)\n"); +T(" p REG_SHELL_PATH explicit / match\n"); +T(" q REG_DELIMITED delimited pattern\n"); +T(" r REG_RIGHT implicit ...$\n"); +T(" s REG_SHELL_ESCAPED \\ not special\n"); +T(" t REG_MUSTDELIM all delimiters must be specified\n"); +T(" u standard unspecified behavior -- errors not counted\n"); +T(" v REG_CLASS_ESCAPE \\ special inside [...]\n"); +T(" w REG_NOSUB no subexpression match array\n"); +T(" x REG_LENIENT let some errors slide\n"); +T(" y REG_LEFT regexec() implicit ^...\n"); +T(" z REG_NULL NULL subexpressions ok\n"); +T(" $ expand C \\c escapes in fields 2 and 3\n"); +T(" / field 2 is a regsubcomp() expression\n"); +T(" = field 3 is a regdecomp() expression\n"); +T("\n"); +T(" Field 1 control lines:\n"); +T("\n"); +T(" C set LC_COLLATE and LC_CTYPE to locale in field 2\n"); +T("\n"); +T(" ?test ... output field 5 if passed and != EXPECTED, silent otherwise\n"); +T(" &test ... output field 5 if current and previous passed\n"); +T(" |test ... output field 5 if current passed and previous failed\n"); +T(" ; ... output field 2 if previous failed\n"); +T(" {test ... skip if failed until }\n"); +T(" } end of skip\n"); +T("\n"); +T(" : comment comment copied as output NOTE\n"); +T(" :comment:test :comment: ignored\n"); +T(" N[OTE] comment comment copied as output NOTE\n"); +T(" T[EST] comment comment\n"); +T("\n"); +T(" number use number for nmatch (20 by default)\n"); +T("\n"); +T(" Field 2: the regular expression pattern; SAME uses the pattern from\n"); +T(" the previous specification. RE_DUP_MAX inside {...} expands to the\n"); +T(" value from <limits.h>.\n"); +T("\n"); +T(" Field 3: the string to match. X...{RE_DUP_MAX} expands to RE_DUP_MAX\n"); +T(" copies of X.\n"); +T("\n"); +T(" Field 4: the test outcome. This is either one of the posix error\n"); +T(" codes (with REG_ omitted) or the match array, a list of (m,n)\n"); +T(" entries with m and n being first and last+1 positions in the\n"); +T(" field 3 string, or NULL if REG_NOSUB is in effect and success\n"); +T(" is expected. BADPAT is acceptable in place of any regcomp(3)\n"); +T(" error code. The match[] array is initialized to (-2,-2) before\n"); +T(" each test. All array elements from 0 to nmatch-1 must be specified\n"); +T(" in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n"); +T(" Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n"); +T(" matched (?{...}) expression, where x is the text enclosed by {...},\n"); +T(" o is the expression ordinal counting from 1, and n is the length of\n"); +T(" the unmatched portion of the subject string. If x starts with a\n"); +T(" number then that is the return value of re_execf(), otherwise 0 is\n"); +T(" returned. RE_DUP_MAX[-+]N expands to the <limits.h> value -+N.\n"); +T("\n"); +T(" Field 5: optional comment appended to the report.\n"); +T("\n"); +T("CAVEAT\n"); +T(" If a regex implementation misbehaves with memory then all bets are off.\n"); +T("\n"); +T("CONTRIBUTORS\n"); +T(" Glenn Fowler gsf@research.att.com (ksh strmatch, regex extensions)\n"); +T(" David Korn dgk@research.att.com (ksh glob matcher)\n"); +T(" Doug McIlroy mcilroy@dartmouth.edu (ast regex/testre in C++)\n"); +T(" Tom Lord lord@regexps.com (rx tests)\n"); +T(" Henry Spencer henry@zoo.toronto.edu (original public regex)\n"); +T(" Andrew Hume andrew@research.att.com (gre tests)\n"); +T(" John Maddock John_Maddock@compuserve.com (regex++ tests)\n"); +T(" Philip Hazel ph10@cam.ac.uk (pcre tests)\n"); +T(" Ville Laurikari vl@iki.fi (libtre tests)\n"); +H("</PRE>\n"); +H("</BODY>\n"); +H("</HTML>\n"); +} + +#ifndef elementsof +#define elementsof(x) (sizeof(x)/sizeof(x[0])) +#endif + +#ifndef streq +#define streq(a,b) (*(a)==*(b)&&!strcmp(a,b)) +#endif + +#define HUNG 2 +#define NOTEST (~0) + +#ifndef REG_TEST_DEFAULT +#define REG_TEST_DEFAULT 0 +#endif + +#ifndef REG_EXEC_DEFAULT +#define REG_EXEC_DEFAULT 0 +#endif + +static const char* unsupported[] = +{ + "BASIC", +#ifndef REG_EXTENDED + "EXTENDED", +#endif +#ifndef REG_AUGMENTED + "AUGMENTED", +#endif +#ifndef REG_SHELL + "SHELL", +#endif + +#ifndef REG_CLASS_ESCAPE + "CLASS_ESCAPE", +#endif +#ifndef REG_COMMENT + "COMMENT", +#endif +#ifndef REG_DELIMITED + "DELIMITED", +#endif +#ifndef REG_DISCIPLINE + "DISCIPLINE", +#endif +#ifndef REG_ESCAPE + "ESCAPE", +#endif +#ifndef REG_ICASE + "ICASE", +#endif +#ifndef REG_LEFT + "LEFT", +#endif +#ifndef REG_LENIENT + "LENIENT", +#endif +#ifndef REG_LITERAL + "LITERAL", +#endif +#ifndef REG_MINIMAL + "MINIMAL", +#endif +#ifndef REG_MULTIPLE + "MULTIPLE", +#endif +#ifndef REG_MULTIREF + "MULTIREF", +#endif +#ifndef REG_MUSTDELIM + "MUSTDELIM", +#endif +#ifndef REG_NEWLINE + "NEWLINE", +#endif +#ifndef REG_NOTBOL + "NOTBOL", +#endif +#ifndef REG_NOTEOL + "NOTEOL", +#endif +#ifndef REG_NULL + "NULL", +#endif +#ifndef REG_RIGHT + "RIGHT", +#endif +#ifndef REG_SHELL_DOT + "SHELL_DOT", +#endif +#ifndef REG_SHELL_ESCAPED + "SHELL_ESCAPED", +#endif +#ifndef REG_SHELL_GROUP + "SHELL_GROUP", +#endif +#ifndef REG_SHELL_PATH + "SHELL_PATH", +#endif +#ifndef REG_SPAN + "SPAN", +#endif +#if REG_NOSUB & REG_TEST_DEFAULT + "SUBMATCH", +#endif +#if !_REG_nexec + "regnexec", +#endif +#if !_REG_subcomp + "regsubcomp", +#endif +#if !_REG_decomp + "redecomp", +#endif + 0 +}; + +#ifndef REG_CLASS_ESCAPE +#define REG_CLASS_ESCAPE NOTEST +#endif +#ifndef REG_COMMENT +#define REG_COMMENT NOTEST +#endif +#ifndef REG_DELIMITED +#define REG_DELIMITED NOTEST +#endif +#ifndef REG_ESCAPE +#define REG_ESCAPE NOTEST +#endif +#ifndef REG_ICASE +#define REG_ICASE NOTEST +#endif +#ifndef REG_LEFT +#define REG_LEFT NOTEST +#endif +#ifndef REG_LENIENT +#define REG_LENIENT 0 +#endif +#ifndef REG_MINIMAL +#define REG_MINIMAL NOTEST +#endif +#ifndef REG_MULTIPLE +#define REG_MULTIPLE NOTEST +#endif +#ifndef REG_MULTIREF +#define REG_MULTIREF NOTEST +#endif +#ifndef REG_MUSTDELIM +#define REG_MUSTDELIM NOTEST +#endif +#ifndef REG_NEWLINE +#define REG_NEWLINE NOTEST +#endif +#ifndef REG_NOTBOL +#define REG_NOTBOL NOTEST +#endif +#ifndef REG_NOTEOL +#define REG_NOTEOL NOTEST +#endif +#ifndef REG_NULL +#define REG_NULL NOTEST +#endif +#ifndef REG_RIGHT +#define REG_RIGHT NOTEST +#endif +#ifndef REG_SHELL_DOT +#define REG_SHELL_DOT NOTEST +#endif +#ifndef REG_SHELL_ESCAPED +#define REG_SHELL_ESCAPED NOTEST +#endif +#ifndef REG_SHELL_GROUP +#define REG_SHELL_GROUP NOTEST +#endif +#ifndef REG_SHELL_PATH +#define REG_SHELL_PATH NOTEST +#endif +#ifndef REG_SPAN +#define REG_SPAN NOTEST +#endif + +#define REG_UNKNOWN (-1) + +#ifndef REG_ENEWLINE +#define REG_ENEWLINE (REG_UNKNOWN-1) +#endif +#ifndef REG_ENULL +#ifndef REG_EMPTY +#define REG_ENULL (REG_UNKNOWN-2) +#else +#define REG_ENULL REG_EMPTY +#endif +#endif +#ifndef REG_ECOUNT +#define REG_ECOUNT (REG_UNKNOWN-3) +#endif +#ifndef REG_BADESC +#define REG_BADESC (REG_UNKNOWN-4) +#endif +#ifndef REG_EMEM +#define REG_EMEM (REG_UNKNOWN-5) +#endif +#ifndef REG_EHUNG +#define REG_EHUNG (REG_UNKNOWN-6) +#endif +#ifndef REG_EBUS +#define REG_EBUS (REG_UNKNOWN-7) +#endif +#ifndef REG_EFAULT +#define REG_EFAULT (REG_UNKNOWN-8) +#endif +#ifndef REG_EFLAGS +#define REG_EFLAGS (REG_UNKNOWN-9) +#endif +#ifndef REG_EDELIM +#define REG_EDELIM (REG_UNKNOWN-9) +#endif + +static const struct { int code; char* name; } codes[] = +{ + REG_UNKNOWN, "UNKNOWN", + REG_NOMATCH, "NOMATCH", + REG_BADPAT, "BADPAT", + REG_ECOLLATE, "ECOLLATE", + REG_ECTYPE, "ECTYPE", + REG_EESCAPE, "EESCAPE", + REG_ESUBREG, "ESUBREG", + REG_EBRACK, "EBRACK", + REG_EPAREN, "EPAREN", + REG_EBRACE, "EBRACE", + REG_BADBR, "BADBR", + REG_ERANGE, "ERANGE", + REG_ESPACE, "ESPACE", + REG_BADRPT, "BADRPT", + REG_ENEWLINE, "ENEWLINE", + REG_ENULL, "ENULL", + REG_ECOUNT, "ECOUNT", + REG_BADESC, "BADESC", + REG_EMEM, "EMEM", + REG_EHUNG, "EHUNG", + REG_EBUS, "EBUS", + REG_EFAULT, "EFAULT", + REG_EFLAGS, "EFLAGS", + REG_EDELIM, "EDELIM", +}; + +static struct +{ + regmatch_t NOMATCH; + int errors; + int extracted; + int ignored; + int lineno; + int passed; + int signals; + int unspecified; + int verify; + int warnings; + char* file; + char* stack; + char* which; + jmp_buf gotcha; +#ifdef REG_DISCIPLINE + Disc_t disc; +#endif +} state; + +static void +quote(char* s, int len, unsigned long test) +{ + unsigned char* u = (unsigned char*)s; + unsigned char* e; + int c; +#ifdef MB_CUR_MAX + int w; +#endif + + if (!u) + printf("NIL"); + else if (!*u && len <= 1) + printf("NULL"); + else if (test & TEST_EXPAND) + { + if (len < 0) + len = strlen((char*)u); + e = u + len; + if (test & TEST_DELIMIT) + printf("\""); + while (u < e) + switch (c = *u++) + { + case '\\': + printf("\\\\"); + break; + case '"': + if (test & TEST_DELIMIT) + printf("\\\""); + else + printf("\""); + break; + case '\a': + printf("\\a"); + break; + case '\b': + printf("\\b"); + break; + case 033: + printf("\\e"); + break; + case '\f': + printf("\\f"); + break; + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + case '\v': + printf("\\v"); + break; + default: +#ifdef MB_CUR_MAX + s = (char*)u - 1; + if ((w = mblen(s, (char*)e - s)) > 1) + { + u += w - 1; + fwrite(s, 1, w, stdout); + } + else +#endif + if (!iscntrl(c) && isprint(c)) + putchar(c); + else + printf("\\x%02x", c); + break; + } + if (test & TEST_DELIMIT) + printf("\""); + } + else + printf("%s", s); +} + +static void +report(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, unsigned long test) +{ + if (state.file) + printf("%s:", state.file); + printf("%d:", state.lineno); + if (re) + { + printf(" "); + quote(re, -1, test|TEST_DELIMIT); + if (s) + { + printf(" versus "); + quote(s, len, test|TEST_DELIMIT); + } + } + if (test & TEST_UNSPECIFIED) + { + state.unspecified++; + printf(" unspecified behavior"); + } + else + state.errors++; + if (state.which) + printf(" %s", state.which); + if (flags & REG_NOSUB) + printf(" NOSUB"); + if (fun) + printf(" %s", fun); + if (comment[strlen(comment)-1] == '\n') + printf(" %s", comment); + else + { + printf(" %s: ", comment); + if (msg) + printf("%s: ", msg); + } +} + +static void +error(regex_t* preg, int code) +{ + char* msg; + char buf[256]; + + switch (code) + { + case REG_EBUS: + msg = "bus error"; + break; + case REG_EFAULT: + msg = "memory fault"; + break; + case REG_EHUNG: + msg = "did not terminate"; + break; + default: + regerror(code, preg, msg = buf, sizeof buf); + break; + } + printf("%s\n", msg); +} + +static void +bad(char* comment, char* re, char* s, int len, unsigned long test) +{ + printf("bad test case "); + report(comment, NiL, re, s, len, NiL, 0, test); + exit(1); +} + +static int +escape(char* s) +{ + char* b; + char* t; + char* q; + char* e; + int c; + + for (b = t = s; *t = *s; s++, t++) + if (*s == '\\') + switch (*++s) + { + case '\\': + break; + case 'a': + *t = '\a'; + break; + case 'b': + *t = '\b'; + break; + case 'c': + if (*t = *++s) + *t &= 037; + else + s--; + break; + case 'e': + case 'E': + *t = 033; + break; + case 'f': + *t = '\f'; + break; + case 'n': + *t = '\n'; + break; + case 'r': + *t = '\r'; + break; + case 's': + *t = ' '; + break; + case 't': + *t = '\t'; + break; + case 'v': + *t = '\v'; + break; + case 'u': + case 'x': + c = 0; + q = c == 'u' ? (s + 5) : (char*)0; + e = s + 1; + while (!e || !q || s < q) + { + switch (*++s) + { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + c = (c << 4) + *s - 'a' + 10; + continue; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + c = (c << 4) + *s - 'A' + 10; + continue; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c = (c << 4) + *s - '0'; + continue; + case '{': + case '[': + if (s != e) + { + s--; + break; + } + e = 0; + continue; + case '}': + case ']': + if (e) + s--; + break; + default: + s--; + break; + } + break; + } + *t = c; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = *s - '0'; + q = s + 2; + while (s < q) + { + switch (*++s) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = (c << 3) + *s - '0'; + break; + default: + q = --s; + break; + } + } + *t = c; + break; + default: + *(s + 1) = 0; + bad("invalid C \\ escape\n", s - 1, NiL, 0, 0); + } + return t - b; +} + +static void +matchoffprint(int off) +{ + switch (off) + { + case -2: + printf("X"); + break; + case -1: + printf("?"); + break; + default: + printf("%d", off); + break; + } +} + +static void +matchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test) +{ + int i; + + for (; nmatch > nsub + 1; nmatch--) + if ((match[nmatch-1].rm_so != -1 || match[nmatch-1].rm_eo != -1) && (!(test & TEST_IGNORE_POSITION) || match[nmatch-1].rm_so >= 0 && match[nmatch-1].rm_eo >= 0)) + break; + for (i = 0; i < nmatch; i++) + { + printf("("); + matchoffprint(match[i].rm_so); + printf(","); + matchoffprint(match[i].rm_eo); + printf(")"); + } + if (!(test & (TEST_ACTUAL|TEST_BASELINE))) + { + if (ans) + printf(" expected: %s", ans); + printf("\n"); + } +} + +static int +matchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, int len, int flags, unsigned long test) +{ + char* p; + int i; + int m; + int n; + + if (streq(ans, "OK")) + return test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY); + for (i = 0, p = ans; i < nmatch && *p; i++) + { + if (*p == '{') + { +#ifdef REG_DISCIPLINE + char* x; + + if (!(x = sfstruse(state.disc.sp))) + bad("out of space [discipline string]\n", NiL, NiL, 0, 0); + if (strcmp(p, x)) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + return 0; + report("callout failed", NiL, re, s, len, NiL, flags, test); + quote(p, -1, test); + printf(" expected, "); + quote(x, -1, test); + printf(" returned\n"); + } +#endif + break; + } + if (*p++ != '(') + bad("improper answer\n", re, s, -1, test); + if (*p == '?') + { + m = -1; + p++; + } + else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) + { + m = RE_DUP_MAX; + p += 10; + if (*p == '+' || *p == '-') + m += strtol(p, &p, 10); + } + else + m = strtol(p, &p, 10); + if (*p++ != ',') + bad("improper answer\n", re, s, -1, test); + if (*p == '?') + { + n = -1; + p++; + } + else if (*p == 'R' && !memcmp(p, "RE_DUP_MAX", 10)) + { + n = RE_DUP_MAX; + p += 10; + if (*p == '+' || *p == '-') + n += strtol(p, &p, 10); + } + else + n = strtol(p, &p, 10); + if (*p++ != ')') + bad("improper answer\n", re, s, -1, test); + if (m!=match[i].rm_so || n!=match[i].rm_eo) + { + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) + { + report("failed: match was", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch, nsub, ans, test); + } + return 0; + } + } + for (; i < nmatch; i++) + { + if (match[i].rm_so!=-1 || match[i].rm_eo!=-1) + { + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_VERIFY))) + { + if ((test & TEST_IGNORE_POSITION) && (match[i].rm_so<0 || match[i].rm_eo<0)) + { + state.ignored++; + return 0; + } + if (!(test & TEST_SUMMARY)) + { + report("failed: match was", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch, nsub, ans, test); + } + } + return 0; + } + } + if (!(test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so) + { + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY))) + { + report("failed: overran match array", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch + 1, nsub, NiL, test); + } + return 0; + } + return 1; +} + +static void +sigunblock(int s) +{ +#ifdef SIG_SETMASK + int op; + sigset_t mask; + + sigemptyset(&mask); + if (s) + { + sigaddset(&mask, s); + op = SIG_UNBLOCK; + } + else op = SIG_SETMASK; + sigprocmask(op, &mask, NiL); +#else +#ifdef sigmask + sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L); +#endif +#endif +} + +static void +gotcha(int sig) +{ + int ret; + + signal(sig, gotcha); + alarm(0); + state.signals++; + switch (sig) + { + case SIGALRM: + ret = REG_EHUNG; + break; + case SIGBUS: + ret = REG_EBUS; + break; + default: + ret = REG_EFAULT; + break; + } + sigunblock(sig); + longjmp(state.gotcha, ret); +} + +static char* +getline(FILE* fp) +{ + static char buf[32 * 1024]; + + register char* s = buf; + register char* e = &buf[sizeof(buf)]; + register char* b; + + for (;;) + { + if (!(b = fgets(s, e - s, fp))) + return 0; + state.lineno++; + s += strlen(s); + if (s == b || *--s != '\n' || s == b || *(s - 1) != '\\') + { + *s = 0; + break; + } + s--; + } + return buf; +} + +static unsigned long +note(unsigned long level, char* msg, unsigned long skip, unsigned long test) +{ + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY)) && !skip) + { + printf("NOTE\t"); + if (msg) + printf("%s: ", msg); + printf("skipping lines %d", state.lineno); + } + return skip | level; +} + +#define TABS(n) &ts[7-((n)&7)] + +static char ts[] = "\t\t\t\t\t\t\t"; + +static unsigned long +extract(int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) +{ + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_OK|TEST_PASS|TEST_SUMMARY)) + { + state.extracted = 1; + if (test & TEST_OK) + { + state.passed++; + if ((test & TEST_VERIFY) && !(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) + { + if (msg && strcmp(msg, "EXPECTED")) + printf("NOTE\t%s\n", msg); + return skip; + } + test &= ~(TEST_PASS|TEST_QUERY); + } + if (test & (TEST_QUERY|TEST_VERIFY)) + { + if (test & TEST_BASELINE) + test &= ~(TEST_BASELINE|TEST_PASS); + else + test |= TEST_PASS; + skip |= level; + } + if (!(test & TEST_OK)) + { + if (test & TEST_UNSPECIFIED) + state.unspecified++; + else + state.errors++; + } + if (test & (TEST_PASS|TEST_SUMMARY)) + return skip; + test &= ~TEST_DELIMIT; + printf("%s%s", spec, TABS(*tabs++)); + if ((test & (TEST_BASELINE|TEST_SAME)) == (TEST_BASELINE|TEST_SAME)) + printf("SAME"); + else + quote(re, -1, test); + printf("%s", TABS(*tabs++)); + quote(s, -1, test); + printf("%s", TABS(*tabs++)); + if (!(test & (TEST_ACTUAL|TEST_BASELINE)) || !accept && !match) + printf("%s", ans); + else if (accept) + printf("%s", accept); + else + matchprint(match, nmatch, nsub, NiL, test); + if (msg) + printf("%s%s", TABS(*tabs++), msg); + putchar('\n'); + } + else if (test & TEST_QUERY) + skip = note(level, msg, skip, test); + else if (test & TEST_VERIFY) + state.extracted = 1; + return skip; +} + +static int +catchfree(regex_t* preg, int flags, int* tabs, char* spec, char* re, char* s, char* ans, char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, unsigned long skip, unsigned long level, unsigned long test) +{ + int eret; + + if (!(test & TEST_CATCH)) + { + regfree(preg); + eret = 0; + } + else if (!(eret = setjmp(state.gotcha))) + { + alarm(HUNG); + regfree(preg); + alarm(0); + } + else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + extract(tabs, spec, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + else + { + report("failed", "regfree", re, NiL, -1, msg, flags, test); + error(preg, eret); + } + return eret; +} + +static char* +expand(char* os, char* ot) +{ + char* s = os; + char* t; + int n = 0; + int r; + long m; + + for (;;) + { + switch (*s++) + { + case 0: + break; + case '{': + n++; + continue; + case '}': + n--; + continue; + case 'R': + if (n == 1 && !memcmp(s, "E_DUP_MAX", 9)) + { + s--; + for (t = ot; os < s; *t++ = *os++); + r = ((t - ot) >= 5 && t[-1] == '{' && t[-2] == '.' && t[-3] == '.' && t[-4] == '.') ? t[-5] : 0; + os = ot; + m = RE_DUP_MAX; + if (*(s += 10) == '+' || *s == '-') + m += strtol(s, &s, 10); + if (r) + { + t -= 5; + while (m-- > 0) + *t++ = r; + while (*s && *s++ != '}'); + } + else + t += snprintf(t, 32, "%ld", m); + while (*t = *s++) + t++; + break; + } + continue; + default: + continue; + } + break; + } + return os; +} + +int +main(int argc, char** argv) +{ + int flags; + int cflags; + int eflags; + int nmatch; + int nexec; + int nstr; + int cret; + int eret; + int nsub; + int i; + int j; + int expected; + int got; + int locale; + int subunitlen; + int testno; + unsigned long level; + unsigned long skip; + char* p; + char* line; + char* spec; + char* re; + char* s; + char* ans; + char* msg; + char* fun; + char* ppat; + char* subunit; + char* version; + char* field[6]; + char* delim[6]; + FILE* fp; + int tabs[6]; + char unit[64]; + regmatch_t match[100]; + regex_t preg; + + static char pat[32 * 1024]; + static char patbuf[32 * 1024]; + static char strbuf[32 * 1024]; + + int nonosub = REG_NOSUB == 0; + int nonexec = 0; + + unsigned long test = 0; + + static char* filter[] = { "-", 0 }; + + state.NOMATCH.rm_so = state.NOMATCH.rm_eo = -2; + p = unit; + version = (char*)id + 10; + while (p < &unit[sizeof(unit)-1] && (*p = *version++) && !isspace(*p)) + p++; + *p = 0; + while ((p = *++argv) && *p == '-') + for (;;) + { + switch (*++p) + { + case 0: + break; + case 'c': + test |= TEST_CATCH; + continue; + case 'e': + test |= TEST_IGNORE_ERROR; + continue; + case 'h': + case '?': + help(0); + return 2; + case '-': + help(p[1] == 'h'); + return 2; + case 'n': + nonexec = 1; + continue; + case 'o': + test |= TEST_IGNORE_OVER; + continue; + case 'p': + test |= TEST_IGNORE_POSITION; + continue; + case 's': +#ifdef REG_DISCIPLINE + if (!(state.stack = stkalloc(stkstd, 0))) + fprintf(stderr, "%s: out of space [stack]", unit); + state.disc.disc.re_resizef = resizef; + state.disc.disc.re_resizehandle = (void*)stkstd; +#endif + continue; + case 'x': + nonosub = 1; + continue; + case 'v': + test |= TEST_VERBOSE; + continue; + case 'A': + test |= TEST_ACTUAL; + continue; + case 'B': + test |= TEST_BASELINE; + continue; + case 'F': + test |= TEST_FAIL; + continue; + case 'P': + test |= TEST_PASS; + continue; + case 'S': + test |= TEST_SUMMARY; + continue; + default: + fprintf(stderr, "%s: %c: invalid option\n", unit, *p); + return 2; + } + break; + } + if (!*argv) + argv = filter; + locale = 0; + while (state.file = *argv++) + { + if (streq(state.file, "-") || streq(state.file, "/dev/stdin") || streq(state.file, "/dev/fd/0")) + { + state.file = 0; + fp = stdin; + } + else if (!(fp = fopen(state.file, "r"))) + { + fprintf(stderr, "%s: %s: cannot read\n", unit, state.file); + return 2; + } + testno = state.errors = state.ignored = state.lineno = state.passed = + state.signals = state.unspecified = state.warnings = 0; + skip = 0; + level = 1; + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) + { + printf("TEST\t%s ", unit); + if (s = state.file) + { + subunit = p = 0; + for (;;) + { + switch (*s++) + { + case 0: + break; + case '/': + subunit = s; + continue; + case '.': + p = s - 1; + continue; + default: + continue; + } + break; + } + if (!subunit) + subunit = state.file; + if (p < subunit) + p = s - 1; + subunitlen = p - subunit; + printf("%-.*s ", subunitlen, subunit); + } + else + subunit = 0; + for (s = version; *s && (*s != ' ' || *(s + 1) != '$'); s++) + putchar(*s); + if (test & TEST_CATCH) + printf(", catch"); + if (test & TEST_IGNORE_ERROR) + printf(", ignore error code mismatches"); + if (test & TEST_IGNORE_POSITION) + printf(", ignore negative position mismatches"); +#ifdef REG_DISCIPLINE + if (state.stack) + printf(", stack"); +#endif + if (test & TEST_VERBOSE) + printf(", verbose"); + printf("\n"); +#ifdef REG_VERSIONID + if (regerror(REG_VERSIONID, NiL, pat, sizeof(pat)) > 0) + s = pat; + else +#endif +#ifdef REG_TEST_VERSION + s = REG_TEST_VERSION; +#else + s = "regex"; +#endif + printf("NOTE\t%s\n", s); + if (elementsof(unsupported) > 1) + { +#if (REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) || !defined(REG_EXTENDED) + i = 0; +#else + i = REG_EXTENDED != 0; +#endif + for (got = 0; i < elementsof(unsupported) - 1; i++) + { + if (!got) + { + got = 1; + printf("NOTE\tunsupported: %s", unsupported[i]); + } + else + printf(",%s", unsupported[i]); + } + if (got) + printf("\n"); + } + } +#ifdef REG_DISCIPLINE + state.disc.disc.re_version = REG_VERSION; + state.disc.disc.re_compf = compf; + state.disc.disc.re_execf = execf; + if (!(state.disc.sp = sfstropen())) + bad("out of space [discipline string stream]\n", NiL, NiL, 0, 0); + preg.re_disc = &state.disc.disc; +#endif + if (test & TEST_CATCH) + { + signal(SIGALRM, gotcha); + signal(SIGBUS, gotcha); + signal(SIGSEGV, gotcha); + } + while (p = getline(fp)) + { + + /* parse: */ + + line = p; + if (*p == ':' && !isspace(*(p + 1))) + { + while (*++p && *p != ':'); + if (!*p++) + { + if (test & TEST_BASELINE) + printf("%s\n", line); + continue; + } + } + while (isspace(*p)) + p++; + if (*p == 0 || *p == '#' || *p == 'T') + { + if (test & TEST_BASELINE) + printf("%s\n", line); + continue; + } + if (*p == ':' || *p == 'N') + { + if (test & TEST_BASELINE) + printf("%s\n", line); + else if (!(test & (TEST_ACTUAL|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) + { + while (*++p && !isspace(*p)); + while (isspace(*p)) + p++; + printf("NOTE %s\n", p); + } + continue; + } + j = 0; + i = 0; + field[i++] = p; + for (;;) + { + switch (*p++) + { + case 0: + p--; + j = 0; + goto checkfield; + case '\t': + *(delim[i] = p - 1) = 0; + j = 1; + checkfield: + s = field[i - 1]; + if (streq(s, "NIL")) + field[i - 1] = 0; + else if (streq(s, "NULL")) + *s = 0; + while (*p == '\t') + { + p++; + j++; + } + tabs[i - 1] = j; + if (!*p) + break; + if (i >= elementsof(field)) + bad("too many fields\n", NiL, NiL, 0, 0); + field[i++] = p; + /*FALLTHROUGH*/ + default: + continue; + } + break; + } + if (!(spec = field[0])) + bad("NIL spec\n", NiL, NiL, 0, 0); + + /* interpret: */ + + cflags = REG_TEST_DEFAULT; + eflags = REG_EXEC_DEFAULT; + test &= TEST_GLOBAL; + state.extracted = 0; + nmatch = 20; + nsub = -1; + for (p = spec; *p; p++) + { + if (isdigit(*p)) + { + nmatch = strtol(p, &p, 10); + if (nmatch >= elementsof(match)) + bad("nmatch must be < 100\n", NiL, NiL, 0, 0); + p--; + continue; + } + switch (*p) + { + case 'A': + test |= TEST_ARE; + continue; + case 'B': + test |= TEST_BRE; + continue; + case 'C': + if (!(test & TEST_QUERY) && !(skip & level)) + bad("locale must be nested\n", NiL, NiL, 0, 0); + test &= ~TEST_QUERY; + if (locale) + bad("locale nesting not supported\n", NiL, NiL, 0, 0); + if (i != 2) + bad("locale field expected\n", NiL, NiL, 0, 0); + if (!(skip & level)) + { +#if defined(LC_COLLATE) && defined(LC_CTYPE) + s = field[1]; + if (!s || streq(s, "POSIX")) + s = "C"; + if ((ans = setlocale(LC_COLLATE, s)) && streq(ans, "POSIX")) + ans = "C"; + if (!ans || !streq(ans, s) && streq(s, "C")) + ans = 0; + else if ((ans = setlocale(LC_CTYPE, s)) && streq(ans, "POSIX")) + ans = "C"; + if (!ans || !streq(ans, s) && streq(s, "C")) + skip = note(level, s, skip, test); + else + { + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) + printf("NOTE \"%s\" locale\n", s); + locale = level; + } +#else + skip = note(level, skip, test, "locales not supported"); +#endif + } + cflags = NOTEST; + continue; + case 'E': + test |= TEST_ERE; + continue; + case 'K': + test |= TEST_KRE; + continue; + case 'L': + test |= TEST_LRE; + continue; + case 'S': + test |= TEST_SRE; + continue; + + case 'a': + cflags |= REG_LEFT|REG_RIGHT; + continue; + case 'b': + eflags |= REG_NOTBOL; + continue; + case 'c': + cflags |= REG_COMMENT; + continue; + case 'd': + cflags |= REG_SHELL_DOT; + continue; + case 'e': + eflags |= REG_NOTEOL; + continue; + case 'f': + cflags |= REG_MULTIPLE; + continue; + case 'g': + cflags |= NOTEST; + continue; + case 'h': + cflags |= REG_MULTIREF; + continue; + case 'i': + cflags |= REG_ICASE; + continue; + case 'j': + cflags |= REG_SPAN; + continue; + case 'k': + cflags |= REG_ESCAPE; + continue; + case 'l': + cflags |= REG_LEFT; + continue; + case 'm': + cflags |= REG_MINIMAL; + continue; + case 'n': + cflags |= REG_NEWLINE; + continue; + case 'o': + cflags |= REG_SHELL_GROUP; + continue; + case 'p': + cflags |= REG_SHELL_PATH; + continue; + case 'q': + cflags |= REG_DELIMITED; + continue; + case 'r': + cflags |= REG_RIGHT; + continue; + case 's': + cflags |= REG_SHELL_ESCAPED; + continue; + case 't': + cflags |= REG_MUSTDELIM; + continue; + case 'u': + test |= TEST_UNSPECIFIED; + continue; + case 'v': + cflags |= REG_CLASS_ESCAPE; + continue; + case 'w': + cflags |= REG_NOSUB; + continue; + case 'x': + if (REG_LENIENT) + cflags |= REG_LENIENT; + else + test |= TEST_LENIENT; + continue; + case 'y': + eflags |= REG_LEFT; + continue; + case 'z': + cflags |= REG_NULL; + continue; + + case '$': + test |= TEST_EXPAND; + continue; + + case '/': + test |= TEST_SUB; + continue; + + case '=': + test |= TEST_DECOMP; + continue; + + case '?': + test |= TEST_VERIFY; + test &= ~(TEST_AND|TEST_OR); + state.verify = state.passed; + continue; + case '&': + test |= TEST_VERIFY|TEST_AND; + test &= ~TEST_OR; + continue; + case '|': + test |= TEST_VERIFY|TEST_OR; + test &= ~TEST_AND; + continue; + case ';': + test |= TEST_OR; + test &= ~TEST_AND; + continue; + + case '{': + level <<= 1; + if (skip & (level >> 1)) + { + skip |= level; + cflags = NOTEST; + } + else + { + skip &= ~level; + test |= TEST_QUERY; + } + continue; + case '}': + if (level == 1) + bad("invalid {...} nesting\n", NiL, NiL, 0, 0); + if ((skip & level) && !(skip & (level>>1))) + { + if (!(test & (TEST_BASELINE|TEST_SUMMARY))) + { + if (test & (TEST_ACTUAL|TEST_FAIL)) + printf("}\n"); + else if (!(test & TEST_PASS)) + printf("-%d\n", state.lineno); + } + } +#if defined(LC_COLLATE) && defined(LC_CTYPE) + else if (locale & level) + { + locale = 0; + if (!(skip & level)) + { + s = "C"; + setlocale(LC_COLLATE, s); + setlocale(LC_CTYPE, s); + if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_SUMMARY))) + printf("NOTE \"%s\" locale\n", s); + else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_PASS)) + printf("}\n"); + } + else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL)) + printf("}\n"); + } +#endif + level >>= 1; + cflags = NOTEST; + continue; + + default: + bad("bad spec\n", spec, NiL, 0, test); + break; + + } + break; + } + if ((cflags|eflags) == NOTEST || (skip & level) && (test & TEST_BASELINE)) + { + if (test & TEST_BASELINE) + { + while (i > 1) + *delim[--i] = '\t'; + printf("%s\n", line); + } + continue; + } + if (test & TEST_OR) + { + if (!(test & TEST_VERIFY)) + { + test &= ~TEST_OR; + if (state.passed == state.verify && i > 1) + printf("NOTE\t%s\n", field[1]); + continue; + } + else if (state.passed > state.verify) + continue; + } + else if (test & TEST_AND) + { + if (state.passed == state.verify) + continue; + state.passed = state.verify; + } + if (i < ((test & TEST_DECOMP) ? 3 : 4)) + bad("too few fields\n", NiL, NiL, 0, test); + while (i < elementsof(field)) + field[i++] = 0; + if (re = field[1]) + { + if (streq(re, "SAME")) + { + re = ppat; + test |= TEST_SAME; + } + else + { + if (test & TEST_EXPAND) + escape(re); + re = expand(re, patbuf); + strcpy(ppat = pat, re); + } + } + else + ppat = 0; + nstr = -1; + if (s = field[2]) + { + s = expand(s, strbuf); + if (test & TEST_EXPAND) + { + nstr = escape(s); +#if _REG_nexec + if (nstr != strlen(s)) + nexec = nstr; +#endif + } + } + if (!(ans = field[(test & TEST_DECOMP) ? 2 : 3])) + bad("NIL answer\n", NiL, NiL, 0, test); + msg = field[4]; + fflush(stdout); + if (test & TEST_SUB) +#if _REG_subcomp + cflags |= REG_DELIMITED; +#else + continue; +#endif +#if !_REG_decomp + if (test & TEST_DECOMP) + continue; +#endif + + compile: + + if (state.extracted || (skip & level)) + continue; +#if !(REG_TEST_DEFAULT & (REG_AUGMENTED|REG_EXTENDED|REG_SHELL)) +#ifdef REG_EXTENDED + if (REG_EXTENDED != 0 && (test & TEST_BRE)) +#else + if (test & TEST_BRE) +#endif + { + test &= ~TEST_BRE; + flags = cflags; + state.which = "BRE"; + } + else +#endif +#ifdef REG_EXTENDED + if (test & TEST_ERE) + { + test &= ~TEST_ERE; + flags = cflags | REG_EXTENDED; + state.which = "ERE"; + } + else +#endif +#ifdef REG_AUGMENTED + if (test & TEST_ARE) + { + test &= ~TEST_ARE; + flags = cflags | REG_AUGMENTED; + state.which = "ARE"; + } + else +#endif +#ifdef REG_LITERAL + if (test & TEST_LRE) + { + test &= ~TEST_LRE; + flags = cflags | REG_LITERAL; + state.which = "LRE"; + } + else +#endif +#ifdef REG_SHELL + if (test & TEST_SRE) + { + test &= ~TEST_SRE; + flags = cflags | REG_SHELL; + state.which = "SRE"; + } + else +#ifdef REG_AUGMENTED + if (test & TEST_KRE) + { + test &= ~TEST_KRE; + flags = cflags | REG_SHELL | REG_AUGMENTED; + state.which = "KRE"; + } + else +#endif +#endif + { + if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) + extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test|TEST_OK); + continue; + } + if ((test & (TEST_QUERY|TEST_VERBOSE|TEST_VERIFY)) == TEST_VERBOSE) + { + printf("test %-3d %s ", state.lineno, state.which); + quote(re, -1, test|TEST_DELIMIT); + printf(" "); + quote(s, nstr, test|TEST_DELIMIT); + printf("\n"); + } + + nosub: + fun = "regcomp"; +#if _REG_nexec + if (nstr >= 0 && nstr != strlen(s)) + nexec = nstr; + + else +#endif + nexec = -1; + if (state.extracted || (skip & level)) + continue; + if (!(test & TEST_QUERY)) + testno++; +#ifdef REG_DISCIPLINE + if (state.stack) + stkset(stkstd, state.stack, 0); + flags |= REG_DISCIPLINE; + state.disc.ordinal = 0; + sfstrseek(state.disc.sp, 0, SEEK_SET); +#endif + if (!(test & TEST_CATCH)) + cret = regcomp(&preg, re, flags); + else if (!(cret = setjmp(state.gotcha))) + { + alarm(HUNG); + cret = regcomp(&preg, re, flags); + alarm(0); + } +#if _REG_subcomp + if (!cret && (test & TEST_SUB)) + { + fun = "regsubcomp"; + p = re + preg.re_npat; + if (!(test & TEST_CATCH)) + cret = regsubcomp(&preg, p, NiL, 0, 0); + else if (!(cret = setjmp(state.gotcha))) + { + alarm(HUNG); + cret = regsubcomp(&preg, p, NiL, 0, 0); + alarm(0); + } + if (!cret && *(p += preg.re_npat) && !(preg.re_sub->re_flags & REG_SUB_LAST)) + { + if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) + continue; + cret = REG_EFLAGS; + } + } +#endif +#if _REG_decomp + if (!cret && (test & TEST_DECOMP)) + { + char buf[128]; + + if ((j = nmatch) > sizeof(buf)) + j = sizeof(buf); + fun = "regdecomp"; + p = re + preg.re_npat; + if (!(test & TEST_CATCH)) + i = regdecomp(&preg, -1, buf, j); + else if (!(cret = setjmp(state.gotcha))) + { + alarm(HUNG); + i = regdecomp(&preg, -1, buf, j); + alarm(0); + } + if (!cret) + { + catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + if (i > j) + { + if (i != (strlen(ans) + 1)) + { + report("failed", fun, re, s, nstr, msg, flags, test); + printf(" %d byte buffer supplied, %d byte buffer required\n", j, i); + } + } + else if (strcmp(buf, ans)) + { + report("failed", fun, re, s, nstr, msg, flags, test); + quote(ans, -1, test|TEST_DELIMIT); + printf(" expected, "); + quote(buf, -1, test|TEST_DELIMIT); + printf(" returned\n"); + } + continue; + } + } +#endif + if (!cret) + { + if (!(flags & REG_NOSUB) && nsub < 0 && *ans == '(') + { + for (p = ans; *p; p++) + if (*p == '(') + nsub++; + else if (*p == '{') + nsub--; + if (nsub >= 0) + { + if (test & TEST_IGNORE_OVER) + { + if (nmatch > nsub) + nmatch = nsub + 1; + } + else if (nsub != preg.re_nsub) + { + if (nsub > preg.re_nsub) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else + { + report("re_nsub incorrect", fun, re, NiL, -1, msg, flags, test); + printf("at least %d expected, %d returned\n", nsub, preg.re_nsub); + state.errors++; + } + } + else + nsub = preg.re_nsub; + } + } + } + if (!(test & (TEST_DECOMP|TEST_SUB)) && *ans && *ans != '(' && !streq(ans, "OK") && !streq(ans, "NOMATCH")) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else if (!(test & TEST_LENIENT)) + { + report("failed", fun, re, NiL, -1, msg, flags, test); + printf("%s expected, OK returned\n", ans); + } + catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + continue; + } + } + else + { + if (test & TEST_LENIENT) + /* we'll let it go this time */; + else if (!*ans || ans[0]=='(' || cret == REG_BADPAT && streq(ans, "NOMATCH")) + { + got = 0; + for (i = 1; i < elementsof(codes); i++) + if (cret==codes[i].code) + got = i; + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else + { + report("failed", fun, re, NiL, -1, msg, flags, test); + printf("%s returned: ", codes[got].name); + error(&preg, cret); + } + } + else + { + expected = got = 0; + for (i = 1; i < elementsof(codes); i++) + { + if (streq(ans, codes[i].name)) + expected = i; + if (cret==codes[i].code) + got = i; + } + if (!expected) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else + { + report("failed: invalid error code", NiL, re, NiL, -1, msg, flags, test); + printf("%s expected, %s returned\n", ans, codes[got].name); + } + } + else if (cret != codes[expected].code && cret != REG_BADPAT) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else if (test & TEST_IGNORE_ERROR) + state.ignored++; + else + { + report("should fail and did", fun, re, NiL, -1, msg, flags, test); + printf("%s expected, %s returned: ", ans, codes[got].name); + state.errors--; + state.warnings++; + error(&preg, cret); + } + } + } + goto compile; + } + +#if _REG_nexec + execute: + if (nexec >= 0) + fun = "regnexec"; + else +#endif + fun = "regexec"; + + for (i = 0; i < elementsof(match); i++) + match[i] = state.NOMATCH; + +#if _REG_nexec + if (nexec >= 0) + { + eret = regnexec(&preg, s, nexec, nmatch, match, eflags); + s[nexec] = 0; + } + else +#endif + { + if (!(test & TEST_CATCH)) + eret = regexec(&preg, s, nmatch, match, eflags); + else if (!(eret = setjmp(state.gotcha))) + { + alarm(HUNG); + eret = regexec(&preg, s, nmatch, match, eflags); + alarm(0); + } + } +#if _REG_subcomp + if ((test & TEST_SUB) && !eret) + { + fun = "regsubexec"; + if (!(test & TEST_CATCH)) + eret = regsubexec(&preg, s, nmatch, match); + else if (!(eret = setjmp(state.gotcha))) + { + alarm(HUNG); + eret = regsubexec(&preg, s, nmatch, match); + alarm(0); + } + } +#endif + if (flags & REG_NOSUB) + { + if (eret) + { + if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, 0, skip, level, test|TEST_DELIMIT); + else + { + report("REG_NOSUB failed", fun, re, s, nstr, msg, flags, test); + error(&preg, eret); + } + } + } + else if (streq(ans, "NOMATCH")) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); + else + { + report("should fail and didn't", fun, re, s, nstr, msg, flags, test); + error(&preg, eret); + } + } + } + else if (eret) + { + if (eret != REG_NOMATCH || !streq(ans, "NOMATCH")) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, nsub, skip, level, test|TEST_DELIMIT); + else + { + report("failed", fun, re, s, nstr, msg, flags, test); + if (eret != REG_NOMATCH) + error(&preg, eret); + else if (*ans) + printf("expected: %s\n", ans); + else + printf("\n"); + } + } + } + else if (streq(ans, "NOMATCH")) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); + else + { + report("should fail and didn't", fun, re, s, nstr, msg, flags, test); + matchprint(match, nmatch, nsub, NiL, test); + } + } +#if _REG_subcomp + else if (test & TEST_SUB) + { + p = preg.re_sub->re_buf; + if (strcmp(p, ans)) + { + report("failed", fun, re, s, nstr, msg, flags, test); + quote(ans, -1, test|TEST_DELIMIT); + printf(" expected, "); + quote(p, -1, test|TEST_DELIMIT); + printf(" returned\n"); + } + } +#endif + else if (!*ans) + { + if (match[0].rm_so != state.NOMATCH.rm_so) + { + if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + else + { + report("failed: no match but match array assigned", NiL, re, s, nstr, msg, flags, test); + matchprint(match, nmatch, nsub, NiL, test); + } + } + } + else if (matchcheck(match, nmatch, nsub, ans, re, s, nstr, flags, test)) + { +#if _REG_nexec + if (nexec < 0 && !nonexec) + { + nexec = nstr >= 0 ? nstr : strlen(s); + s[nexec] = '\n'; + testno++; + goto execute; + } +#endif + if (!(test & (TEST_DECOMP|TEST_SUB|TEST_VERIFY)) && !nonosub) + { + if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) + continue; + flags |= REG_NOSUB; + goto nosub; + } + if (test & (TEST_BASELINE|TEST_PASS|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_OK); + } + else if (test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS|TEST_QUERY|TEST_SUMMARY|TEST_VERIFY)) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, test|TEST_DELIMIT); + if (catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test)) + continue; + goto compile; + } + if (test & TEST_SUMMARY) + printf("tests=%-4d errors=%-4d warnings=%-2d ignored=%-2d unspecified=%-2d signals=%d\n", testno, state.errors, state.warnings, state.ignored, state.unspecified, state.signals); + else if (!(test & (TEST_ACTUAL|TEST_BASELINE|TEST_FAIL|TEST_PASS))) + { + printf("TEST\t%s", unit); + if (subunit) + printf(" %-.*s", subunitlen, subunit); + printf(", %d test%s", testno, testno == 1 ? "" : "s"); + if (state.ignored) + printf(", %d ignored mismatche%s", state.ignored, state.ignored == 1 ? "" : "s"); + if (state.warnings) + printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s"); + if (state.unspecified) + printf(", %d unspecified difference%s", state.unspecified, state.unspecified == 1 ? "" : "s"); + if (state.signals) + printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s"); + printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s"); + } + if (fp != stdin) + fclose(fp); + } + return 0; +} diff --git a/src/pkg/exp/ssh/Makefile b/src/pkg/exp/ssh/Makefile new file mode 100644 index 000000000..e8f33b708 --- /dev/null +++ b/src/pkg/exp/ssh/Makefile @@ -0,0 +1,16 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=exp/ssh +GOFILES=\ + common.go\ + messages.go\ + server.go\ + transport.go\ + channel.go\ + server_shell.go\ + +include ../../../Make.pkg diff --git a/src/pkg/exp/ssh/channel.go b/src/pkg/exp/ssh/channel.go new file mode 100644 index 000000000..922584f63 --- /dev/null +++ b/src/pkg/exp/ssh/channel.go @@ -0,0 +1,317 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "os" + "sync" +) + +// A Channel is an ordered, reliable, duplex stream that is multiplexed over an +// SSH connection. +type Channel interface { + // Accept accepts the channel creation request. + Accept() os.Error + // Reject rejects the channel creation request. After calling this, no + // other methods on the Channel may be called. If they are then the + // peer is likely to signal a protocol error and drop the connection. + Reject(reason RejectionReason, message string) os.Error + + // Read may return a ChannelRequest as an os.Error. + Read(data []byte) (int, os.Error) + Write(data []byte) (int, os.Error) + Close() os.Error + + // AckRequest either sends an ack or nack to the channel request. + AckRequest(ok bool) os.Error + + // ChannelType returns the type of the channel, as supplied by the + // client. + ChannelType() string + // ExtraData returns the arbitary payload for this channel, as supplied + // by the client. This data is specific to the channel type. + ExtraData() []byte +} + +// ChannelRequest represents a request sent on a channel, outside of the normal +// stream of bytes. It may result from calling Read on a Channel. +type ChannelRequest struct { + Request string + WantReply bool + Payload []byte +} + +func (c ChannelRequest) String() string { + return "channel request received" +} + +// RejectionReason is an enumeration used when rejecting channel creation +// requests. See RFC 4254, section 5.1. +type RejectionReason int + +const ( + Prohibited RejectionReason = iota + 1 + ConnectionFailed + UnknownChannelType + ResourceShortage +) + +type channel struct { + // immutable once created + chanType string + extraData []byte + + theyClosed bool + theySentEOF bool + weClosed bool + dead bool + + serverConn *ServerConnection + myId, theirId uint32 + myWindow, theirWindow uint32 + maxPacketSize uint32 + err os.Error + + pendingRequests []ChannelRequest + pendingData []byte + head, length int + + // This lock is inferior to serverConn.lock + lock sync.Mutex + cond *sync.Cond +} + +func (c *channel) Accept() os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + confirm := channelOpenConfirmMsg{ + PeersId: c.theirId, + MyId: c.myId, + MyWindow: c.myWindow, + MaxPacketSize: c.maxPacketSize, + } + return c.serverConn.writePacket(marshal(msgChannelOpenConfirm, confirm)) +} + +func (c *channel) Reject(reason RejectionReason, message string) os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + reject := channelOpenFailureMsg{ + PeersId: c.theirId, + Reason: uint32(reason), + Message: message, + Language: "en", + } + return c.serverConn.writePacket(marshal(msgChannelOpenFailure, reject)) +} + +func (c *channel) handlePacket(packet interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + switch packet := packet.(type) { + case *channelRequestMsg: + req := ChannelRequest{ + Request: packet.Request, + WantReply: packet.WantReply, + Payload: packet.RequestSpecificData, + } + + c.pendingRequests = append(c.pendingRequests, req) + c.cond.Signal() + case *channelCloseMsg: + c.theyClosed = true + c.cond.Signal() + case *channelEOFMsg: + c.theySentEOF = true + c.cond.Signal() + default: + panic("unknown packet type") + } +} + +func (c *channel) handleData(data []byte) { + c.lock.Lock() + defer c.lock.Unlock() + + // The other side should never send us more than our window. + if len(data)+c.length > len(c.pendingData) { + // TODO(agl): we should tear down the channel with a protocol + // error. + return + } + + c.myWindow -= uint32(len(data)) + for i := 0; i < 2; i++ { + tail := c.head + c.length + if tail > len(c.pendingData) { + tail -= len(c.pendingData) + } + n := copy(c.pendingData[tail:], data) + data = data[n:] + c.length += n + } + + c.cond.Signal() +} + +func (c *channel) Read(data []byte) (n int, err os.Error) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.err != nil { + return 0, c.err + } + + if c.myWindow <= uint32(len(c.pendingData))/2 { + packet := marshal(msgChannelWindowAdjust, windowAdjustMsg{ + PeersId: c.theirId, + AdditionalBytes: uint32(len(c.pendingData)) - c.myWindow, + }) + if err := c.serverConn.writePacket(packet); err != nil { + return 0, err + } + } + + for { + if c.theySentEOF || c.theyClosed || c.dead { + return 0, os.EOF + } + + if len(c.pendingRequests) > 0 { + req := c.pendingRequests[0] + if len(c.pendingRequests) == 1 { + c.pendingRequests = nil + } else { + oldPendingRequests := c.pendingRequests + c.pendingRequests = make([]ChannelRequest, len(oldPendingRequests)-1) + copy(c.pendingRequests, oldPendingRequests[1:]) + } + + return 0, req + } + + if c.length > 0 { + tail := c.head + c.length + if tail > len(c.pendingData) { + tail -= len(c.pendingData) + } + n = copy(data, c.pendingData[c.head:tail]) + c.head += n + c.length -= n + if c.head == len(c.pendingData) { + c.head = 0 + } + return + } + + c.cond.Wait() + } + + panic("unreachable") +} + +func (c *channel) Write(data []byte) (n int, err os.Error) { + for len(data) > 0 { + c.lock.Lock() + if c.dead || c.weClosed { + return 0, os.EOF + } + + if c.theirWindow == 0 { + c.cond.Wait() + continue + } + c.lock.Unlock() + + todo := data + if uint32(len(todo)) > c.theirWindow { + todo = todo[:c.theirWindow] + } + + packet := make([]byte, 1+4+4+len(todo)) + packet[0] = msgChannelData + packet[1] = byte(c.theirId) >> 24 + packet[2] = byte(c.theirId) >> 16 + packet[3] = byte(c.theirId) >> 8 + packet[4] = byte(c.theirId) + packet[5] = byte(len(todo)) >> 24 + packet[6] = byte(len(todo)) >> 16 + packet[7] = byte(len(todo)) >> 8 + packet[8] = byte(len(todo)) + copy(packet[9:], todo) + + c.serverConn.lock.Lock() + if err = c.serverConn.writePacket(packet); err != nil { + c.serverConn.lock.Unlock() + return + } + c.serverConn.lock.Unlock() + + n += len(todo) + data = data[len(todo):] + } + + return +} + +func (c *channel) Close() os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + if c.weClosed { + return os.NewError("ssh: channel already closed") + } + c.weClosed = true + + closeMsg := channelCloseMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelClose, closeMsg)) +} + +func (c *channel) AckRequest(ok bool) os.Error { + c.serverConn.lock.Lock() + defer c.serverConn.lock.Unlock() + + if c.serverConn.err != nil { + return c.serverConn.err + } + + if ok { + ack := channelRequestSuccessMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelSuccess, ack)) + } else { + ack := channelRequestFailureMsg{ + PeersId: c.theirId, + } + return c.serverConn.writePacket(marshal(msgChannelFailure, ack)) + } + panic("unreachable") +} + +func (c *channel) ChannelType() string { + return c.chanType +} + +func (c *channel) ExtraData() []byte { + return c.extraData +} diff --git a/src/pkg/exp/ssh/common.go b/src/pkg/exp/ssh/common.go new file mode 100644 index 000000000..698db60b8 --- /dev/null +++ b/src/pkg/exp/ssh/common.go @@ -0,0 +1,96 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "strconv" +) + +// These are string constants in the SSH protocol. +const ( + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + hostAlgoRSA = "ssh-rsa" + cipherAES128CTR = "aes128-ctr" + macSHA196 = "hmac-sha1-96" + compressionNone = "none" + serviceUserAuth = "ssh-userauth" + serviceSSH = "ssh-connection" +) + +// UnexpectedMessageError results when the SSH message that we received didn't +// match what we wanted. +type UnexpectedMessageError struct { + expected, got uint8 +} + +func (u UnexpectedMessageError) String() string { + return "ssh: unexpected message type " + strconv.Itoa(int(u.got)) + " (expected " + strconv.Itoa(int(u.expected)) + ")" +} + +// ParseError results from a malformed SSH message. +type ParseError struct { + msgType uint8 +} + +func (p ParseError) String() string { + return "ssh: parse error in message type " + strconv.Itoa(int(p.msgType)) +} + +func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { + for _, clientAlgo := range clientAlgos { + for _, serverAlgo := range serverAlgos { + if clientAlgo == serverAlgo { + return clientAlgo, true + } + } + } + + return +} + +func findAgreedAlgorithms(clientToServer, serverToClient *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { + kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) + if !ok { + return + } + + hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) + if !ok { + return + } + + clientToServer.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) + if !ok { + return + } + + serverToClient.cipherAlgo, ok = findCommonAlgorithm(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) + if !ok { + return + } + + clientToServer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if !ok { + return + } + + serverToClient.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if !ok { + return + } + + clientToServer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) + if !ok { + return + } + + serverToClient.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) + if !ok { + return + } + + ok = true + return +} diff --git a/src/pkg/exp/ssh/doc.go b/src/pkg/exp/ssh/doc.go new file mode 100644 index 000000000..54a7ba9fd --- /dev/null +++ b/src/pkg/exp/ssh/doc.go @@ -0,0 +1,79 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package ssh implements an SSH server. + +SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. + +An SSH server is represented by a Server, which manages a number of +ServerConnections and handles authentication. + + var s Server + s.PubKeyCallback = pubKeyAuth + s.PasswordCallback = passwordAuth + + pemBytes, err := ioutil.ReadFile("id_rsa") + if err != nil { + panic("Failed to load private key") + } + err = s.SetRSAPrivateKey(pemBytes) + if err != nil { + panic("Failed to parse private key") + } + +Once a Server has been set up, connections can be attached. + + var sConn ServerConnection + sConn.Server = &s + err = sConn.Handshake(conn) + if err != nil { + panic("failed to handshake") + } + +An SSH connection multiplexes several channels, which must be accepted themselves: + + + for { + channel, err := sConn.Accept() + if err != nil { + panic("error from Accept") + } + + ... + } + +Accept reads from the connection, demultiplexes packets to their corresponding +channels and returns when a new channel request is seen. Some goroutine must +always be calling Accept; otherwise no messages will be forwarded to the +channels. + +Channels have a type, depending on the application level protocol intended. In +the case of a shell, the type is "session" and ServerShell may be used to +present a simple terminal interface. + + if channel.ChannelType() != "session" { + c.Reject(UnknownChannelType, "unknown channel type") + return + } + channel.Accept() + + shell := NewServerShell(channel, "> ") + go func() { + defer channel.Close() + for { + line, err := shell.ReadLine() + if err != nil { + break + } + println(line) + } + return + }() +*/ +package ssh diff --git a/src/pkg/exp/ssh/messages.go b/src/pkg/exp/ssh/messages.go new file mode 100644 index 000000000..d375eafae --- /dev/null +++ b/src/pkg/exp/ssh/messages.go @@ -0,0 +1,557 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "big" + "bytes" + "io" + "os" + "reflect" +) + +// These are SSH message type numbers. They are scattered around several +// documents but many were taken from +// http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 +const ( + msgDisconnect = 1 + msgIgnore = 2 + msgUnimplemented = 3 + msgDebug = 4 + msgServiceRequest = 5 + msgServiceAccept = 6 + + msgKexInit = 20 + msgNewKeys = 21 + + msgKexDHInit = 30 + msgKexDHReply = 31 + + msgUserAuthRequest = 50 + msgUserAuthFailure = 51 + msgUserAuthSuccess = 52 + msgUserAuthBanner = 53 + msgUserAuthPubKeyOk = 60 + + msgGlobalRequest = 80 + msgRequestSuccess = 81 + msgRequestFailure = 82 + + msgChannelOpen = 90 + msgChannelOpenConfirm = 91 + msgChannelOpenFailure = 92 + msgChannelWindowAdjust = 93 + msgChannelData = 94 + msgChannelExtendedData = 95 + msgChannelEOF = 96 + msgChannelClose = 97 + msgChannelRequest = 98 + msgChannelSuccess = 99 + msgChannelFailure = 100 +) + +// SSH messages: +// +// These structures mirror the wire format of the corresponding SSH messages. +// They are marshaled using reflection with the marshal and unmarshal functions +// in this file. The only wrinkle is that a final member of type []byte with a +// tag of "rest" receives the remainder of a packet when unmarshaling. + +// See RFC 4253, section 7.1. +type kexInitMsg struct { + Cookie [16]byte + KexAlgos []string + ServerHostKeyAlgos []string + CiphersClientServer []string + CiphersServerClient []string + MACsClientServer []string + MACsServerClient []string + CompressionClientServer []string + CompressionServerClient []string + LanguagesClientServer []string + LanguagesServerClient []string + FirstKexFollows bool + Reserved uint32 +} + +// See RFC 4253, section 8. +type kexDHInitMsg struct { + X *big.Int +} + +type kexDHReplyMsg struct { + HostKey []byte + Y *big.Int + Signature []byte +} + +// See RFC 4253, section 10. +type serviceRequestMsg struct { + Service string +} + +// See RFC 4253, section 10. +type serviceAcceptMsg struct { + Service string +} + +// See RFC 4252, section 5. +type userAuthRequestMsg struct { + User string + Service string + Method string + Payload []byte "rest" +} + +// See RFC 4252, section 5.1 +type userAuthFailureMsg struct { + Methods []string + PartialSuccess bool +} + +// See RFC 4254, section 5.1. +type channelOpenMsg struct { + ChanType string + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte "rest" +} + +// See RFC 4254, section 5.1. +type channelOpenConfirmMsg struct { + PeersId uint32 + MyId uint32 + MyWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte "rest" +} + +// See RFC 4254, section 5.1. +type channelOpenFailureMsg struct { + PeersId uint32 + Reason uint32 + Message string + Language string +} + +type channelRequestMsg struct { + PeersId uint32 + Request string + WantReply bool + RequestSpecificData []byte "rest" +} + +// See RFC 4254, section 5.4. +type channelRequestSuccessMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.4. +type channelRequestFailureMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.3 +type channelCloseMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 5.3 +type channelEOFMsg struct { + PeersId uint32 +} + +// See RFC 4254, section 4 +type globalRequestMsg struct { + Type string + WantReply bool +} + +// See RFC 4254, section 5.2 +type windowAdjustMsg struct { + PeersId uint32 + AdditionalBytes uint32 +} + +// See RFC 4252, section 7 +type userAuthPubKeyOkMsg struct { + Algo string + PubKey string +} + +// unmarshal parses the SSH wire data in packet into out using reflection. +// expectedType is the expected SSH message type. It either returns nil on +// success, or a ParseError or UnexpectedMessageError on error. +func unmarshal(out interface{}, packet []byte, expectedType uint8) os.Error { + if len(packet) == 0 { + return ParseError{expectedType} + } + if packet[0] != expectedType { + return UnexpectedMessageError{expectedType, packet[0]} + } + packet = packet[1:] + + v := reflect.ValueOf(out).Elem() + structType := v.Type() + var ok bool + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + if len(packet) < 1 { + return ParseError{expectedType} + } + field.SetBool(packet[0] != 0) + packet = packet[1:] + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic("array of non-uint8") + } + if len(packet) < t.Len() { + return ParseError{expectedType} + } + for j := 0; j < t.Len(); j++ { + field.Index(j).Set(reflect.ValueOf(packet[j])) + } + packet = packet[t.Len():] + case reflect.Uint32: + var u32 uint32 + if u32, packet, ok = parseUint32(packet); !ok { + return ParseError{expectedType} + } + field.SetUint(uint64(u32)) + case reflect.String: + var s []byte + if s, packet, ok = parseString(packet); !ok { + return ParseError{expectedType} + } + field.SetString(string(s)) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if structType.Field(i).Tag == "rest" { + field.Set(reflect.ValueOf(packet)) + packet = nil + } else { + var s []byte + if s, packet, ok = parseString(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(s)) + } + case reflect.String: + var nl []string + if nl, packet, ok = parseNameList(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(nl)) + default: + panic("slice of unknown type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + if n, packet, ok = parseInt(packet); !ok { + return ParseError{expectedType} + } + field.Set(reflect.ValueOf(n)) + } else { + panic("pointer to unknown type") + } + default: + panic("unknown type") + } + } + + if len(packet) != 0 { + return ParseError{expectedType} + } + + return nil +} + +// marshal serializes the message in msg, using the given message type. +func marshal(msgType uint8, msg interface{}) []byte { + var out []byte + out = append(out, msgType) + + v := reflect.ValueOf(msg) + structType := v.Type() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + var v uint8 + if field.Bool() { + v = 1 + } + out = append(out, v) + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic("array of non-uint8") + } + for j := 0; j < t.Len(); j++ { + out = append(out, byte(field.Index(j).Uint())) + } + case reflect.Uint32: + u32 := uint32(field.Uint()) + out = append(out, byte(u32>>24)) + out = append(out, byte(u32>>16)) + out = append(out, byte(u32>>8)) + out = append(out, byte(u32)) + case reflect.String: + s := field.String() + out = append(out, byte(len(s)>>24)) + out = append(out, byte(len(s)>>16)) + out = append(out, byte(len(s)>>8)) + out = append(out, byte(len(s))) + out = append(out, []byte(s)...) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + length := field.Len() + if structType.Field(i).Tag != "rest" { + out = append(out, byte(length>>24)) + out = append(out, byte(length>>16)) + out = append(out, byte(length>>8)) + out = append(out, byte(length)) + } + for j := 0; j < length; j++ { + out = append(out, byte(field.Index(j).Uint())) + } + case reflect.String: + var length int + for j := 0; j < field.Len(); j++ { + if j != 0 { + length++ /* comma */ + } + length += len(field.Index(j).String()) + } + + out = append(out, byte(length>>24)) + out = append(out, byte(length>>16)) + out = append(out, byte(length>>8)) + out = append(out, byte(length)) + for j := 0; j < field.Len(); j++ { + if j != 0 { + out = append(out, ',') + } + out = append(out, []byte(field.Index(j).String())...) + } + default: + panic("slice of unknown type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + nValue := reflect.ValueOf(&n) + nValue.Elem().Set(field) + needed := intLength(n) + oldLength := len(out) + + if cap(out)-len(out) < needed { + newOut := make([]byte, len(out), 2*(len(out)+needed)) + copy(newOut, out) + out = newOut + } + out = out[:oldLength+needed] + marshalInt(out[oldLength:], n) + } else { + panic("pointer to unknown type") + } + } + } + + return out +} + +var bigOne = big.NewInt(1) + +func parseString(in []byte) (out, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) + if uint32(len(in)) < 4+length { + return + } + out = in[4 : 4+length] + rest = in[4+length:] + ok = true + return +} + +var comma = []byte{','} + +func parseNameList(in []byte) (out []string, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + if len(contents) == 0 { + return + } + parts := bytes.Split(contents, comma) + out = make([]string, len(parts)) + for i, part := range parts { + out[i] = string(part) + } + return +} + +func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + out = new(big.Int) + + if len(contents) > 0 && contents[0]&0x80 == 0x80 { + // This is a negative number + notBytes := make([]byte, len(contents)) + for i := range notBytes { + notBytes[i] = ^contents[i] + } + out.SetBytes(notBytes) + out.Add(out, bigOne) + out.Neg(out) + } else { + // Positive number + out.SetBytes(contents) + } + ok = true + return +} + +func parseUint32(in []byte) (out uint32, rest []byte, ok bool) { + if len(in) < 4 { + return + } + out = uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) + rest = in[4:] + ok = true + return +} + +const maxPacketSize = 36000 + +func nameListLength(namelist []string) int { + length := 4 /* uint32 length prefix */ + for i, name := range namelist { + if i != 0 { + length++ /* comma */ + } + length += len(name) + } + return length +} + +func intLength(n *big.Int) int { + length := 4 /* length bytes */ + if n.Sign() < 0 { + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bitLen := nMinus1.BitLen() + if bitLen%8 == 0 { + // The number will need 0xff padding + length++ + } + length += (bitLen + 7) / 8 + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bitLen := n.BitLen() + if bitLen%8 == 0 { + // The number will need 0x00 padding + length++ + } + length += (bitLen + 7) / 8 + } + + return length +} + +func marshalInt(to []byte, n *big.Int) []byte { + lengthBytes := to + to = to[4:] + length := 0 + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + to[0] = 0xff + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with a 0x00 in order to + // stop it looking like a negative number. + to[0] = 0 + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } + + lengthBytes[0] = byte(length >> 24) + lengthBytes[1] = byte(length >> 16) + lengthBytes[2] = byte(length >> 8) + lengthBytes[3] = byte(length) + return to +} + +func writeInt(w io.Writer, n *big.Int) { + length := intLength(n) + buf := make([]byte, length) + marshalInt(buf, n) + w.Write(buf) +} + +func writeString(w io.Writer, s []byte) { + var lengthBytes [4]byte + lengthBytes[0] = byte(len(s) >> 24) + lengthBytes[1] = byte(len(s) >> 16) + lengthBytes[2] = byte(len(s) >> 8) + lengthBytes[3] = byte(len(s)) + w.Write(lengthBytes[:]) + w.Write(s) +} + +func stringLength(s []byte) int { + return 4 + len(s) +} + +func marshalString(to []byte, s []byte) []byte { + to[0] = byte(len(s) >> 24) + to[1] = byte(len(s) >> 16) + to[2] = byte(len(s) >> 8) + to[3] = byte(len(s)) + to = to[4:] + copy(to, s) + return to[len(s):] +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) diff --git a/src/pkg/exp/ssh/messages_test.go b/src/pkg/exp/ssh/messages_test.go new file mode 100644 index 000000000..629f3d3b1 --- /dev/null +++ b/src/pkg/exp/ssh/messages_test.go @@ -0,0 +1,125 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "big" + "rand" + "reflect" + "testing" + "testing/quick" +) + +var intLengthTests = []struct { + val, length int +}{ + {0, 4 + 0}, + {1, 4 + 1}, + {127, 4 + 1}, + {128, 4 + 2}, + {-1, 4 + 1}, +} + +func TestIntLength(t *testing.T) { + for _, test := range intLengthTests { + v := new(big.Int).SetInt64(int64(test.val)) + length := intLength(v) + if length != test.length { + t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) + } + } +} + +var messageTypes = []interface{}{ + &kexInitMsg{}, + &kexDHInitMsg{}, + &serviceRequestMsg{}, + &serviceAcceptMsg{}, + &userAuthRequestMsg{}, + &channelOpenMsg{}, + &channelOpenConfirmMsg{}, + &channelRequestMsg{}, + &channelRequestSuccessMsg{}, +} + +func TestMarshalUnmarshal(t *testing.T) { + rand := rand.New(rand.NewSource(0)) + for i, iface := range messageTypes { + ty := reflect.ValueOf(iface).Type() + + n := 100 + if testing.Short() { + n = 5 + } + for j := 0; j < n; j++ { + v, ok := quick.Value(ty, rand) + if !ok { + t.Errorf("#%d: failed to create value", i) + break + } + + m1 := v.Elem().Interface() + m2 := iface + + marshaled := marshal(msgIgnore, m1) + if err := unmarshal(m2, marshaled, msgIgnore); err != nil { + t.Errorf("#%d failed to unmarshal %#v: %s", i, m1, err) + break + } + + if !reflect.DeepEqual(v.Interface(), m2) { + t.Errorf("#%d\ngot: %#v\nwant:%#v\n%x", i, m2, m1, marshaled) + break + } + } + } +} + +func randomBytes(out []byte, rand *rand.Rand) { + for i := 0; i < len(out); i++ { + out[i] = byte(rand.Int31()) + } +} + +func randomNameList(rand *rand.Rand) []string { + ret := make([]string, rand.Int31()&15) + for i := range ret { + s := make([]byte, 1+(rand.Int31()&15)) + for j := range s { + s[j] = 'a' + uint8(rand.Int31()&15) + } + ret[i] = string(s) + } + return ret +} + +func randomInt(rand *rand.Rand) *big.Int { + return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) +} + +func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + ki := &kexInitMsg{} + randomBytes(ki.Cookie[:], rand) + ki.KexAlgos = randomNameList(rand) + ki.ServerHostKeyAlgos = randomNameList(rand) + ki.CiphersClientServer = randomNameList(rand) + ki.CiphersServerClient = randomNameList(rand) + ki.MACsClientServer = randomNameList(rand) + ki.MACsServerClient = randomNameList(rand) + ki.CompressionClientServer = randomNameList(rand) + ki.CompressionServerClient = randomNameList(rand) + ki.LanguagesClientServer = randomNameList(rand) + ki.LanguagesServerClient = randomNameList(rand) + if rand.Int31()&1 == 1 { + ki.FirstKexFollows = true + } + return reflect.ValueOf(ki) +} + +func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + dhi := &kexDHInitMsg{} + dhi.X = randomInt(rand) + return reflect.ValueOf(dhi) +} diff --git a/src/pkg/exp/ssh/server.go b/src/pkg/exp/ssh/server.go new file mode 100644 index 000000000..bc0af13e8 --- /dev/null +++ b/src/pkg/exp/ssh/server.go @@ -0,0 +1,714 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "big" + "bufio" + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + _ "crypto/sha1" + "crypto/x509" + "encoding/pem" + "net" + "os" + "sync" +) + +var supportedKexAlgos = []string{kexAlgoDH14SHA1} +var supportedHostKeyAlgos = []string{hostAlgoRSA} +var supportedCiphers = []string{cipherAES128CTR} +var supportedMACs = []string{macSHA196} +var supportedCompressions = []string{compressionNone} + +// Server represents an SSH server. A Server may have several ServerConnections. +type Server struct { + rsa *rsa.PrivateKey + rsaSerialized []byte + + // NoClientAuth is true if clients are allowed to connect without + // authenticating. + NoClientAuth bool + + // PasswordCallback, if non-nil, is called when a user attempts to + // authenticate using a password. It may be called concurrently from + // several goroutines. + PasswordCallback func(user, password string) bool + + // PubKeyCallback, if non-nil, is called when a client attempts public + // key authentication. It must return true iff the given public key is + // valid for the given user. + PubKeyCallback func(user, algo string, pubkey []byte) bool +} + +// SetRSAPrivateKey sets the private key for a Server. A Server must have a +// private key configured in order to accept connections. The private key must +// be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" +// typically contains such a key. +func (s *Server) SetRSAPrivateKey(pemBytes []byte) os.Error { + block, _ := pem.Decode(pemBytes) + if block == nil { + return os.NewError("ssh: no key found") + } + var err os.Error + s.rsa, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + + s.rsaSerialized = marshalRSA(s.rsa) + return nil +} + +// marshalRSA serializes an RSA private key according to RFC 4256, section 6.6. +func marshalRSA(priv *rsa.PrivateKey) []byte { + e := new(big.Int).SetInt64(int64(priv.E)) + length := stringLength([]byte(hostAlgoRSA)) + length += intLength(e) + length += intLength(priv.N) + + ret := make([]byte, length) + r := marshalString(ret, []byte(hostAlgoRSA)) + r = marshalInt(r, e) + r = marshalInt(r, priv.N) + + return ret +} + +// parseRSA parses an RSA key according to RFC 4256, section 6.6. +func parseRSA(in []byte) (pubKey *rsa.PublicKey, ok bool) { + algo, in, ok := parseString(in) + if !ok || string(algo) != hostAlgoRSA { + return nil, false + } + bigE, in, ok := parseInt(in) + if !ok || bigE.BitLen() > 24 { + return nil, false + } + e := bigE.Int64() + if e < 3 || e&1 == 0 { + return nil, false + } + N, in, ok := parseInt(in) + if !ok || len(in) > 0 { + return nil, false + } + return &rsa.PublicKey{ + N: N, + E: int(e), + }, true +} + +func parseRSASig(in []byte) (sig []byte, ok bool) { + algo, in, ok := parseString(in) + if !ok || string(algo) != hostAlgoRSA { + return nil, false + } + sig, in, ok = parseString(in) + if len(in) > 0 { + ok = false + } + return +} + +// cachedPubKey contains the results of querying whether a public key is +// acceptable for a user. The cache only applies to a single ServerConnection. +type cachedPubKey struct { + user, algo string + pubKey []byte + result bool +} + +const maxCachedPubKeys = 16 + +// ServerConnection represents an incomming connection to a Server. +type ServerConnection struct { + Server *Server + + *transport + + channels map[uint32]*channel + nextChanId uint32 + + // lock protects err and also allows Channels to serialise their writes + // to out. + lock sync.RWMutex + err os.Error + + // cachedPubKeys contains the cache results of tests for public keys. + // Since SSH clients will query whether a public key is acceptable + // before attempting to authenticate with it, we end up with duplicate + // queries for public key validity. + cachedPubKeys []cachedPubKey +} + +// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. +type dhGroup struct { + g, p *big.Int +} + +// dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and +// Oakley Group 14 in RFC 3526. +var dhGroup14 *dhGroup + +var dhGroup14Once sync.Once + +func initDHGroup14() { + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + + dhGroup14 = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + } +} + +type handshakeMagics struct { + clientVersion, serverVersion []byte + clientKexInit, serverKexInit []byte +} + +// kexDH performs Diffie-Hellman key agreement on a ServerConnection. The +// returned values are given the same names as in RFC 4253, section 8. +func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err os.Error) { + packet, err := s.readPacket() + if err != nil { + return + } + var kexDHInit kexDHInitMsg + if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { + return + } + + if kexDHInit.X.Sign() == 0 || kexDHInit.X.Cmp(group.p) >= 0 { + return nil, nil, os.NewError("client DH parameter out of bounds") + } + + y, err := rand.Int(rand.Reader, group.p) + if err != nil { + return + } + + Y := new(big.Int).Exp(group.g, y, group.p) + kInt := new(big.Int).Exp(kexDHInit.X, y, group.p) + + var serializedHostKey []byte + switch hostKeyAlgo { + case hostAlgoRSA: + serializedHostKey = s.Server.rsaSerialized + default: + return nil, nil, os.NewError("internal error") + } + + h := hashFunc.New() + writeString(h, magics.clientVersion) + writeString(h, magics.serverVersion) + writeString(h, magics.clientKexInit) + writeString(h, magics.serverKexInit) + writeString(h, serializedHostKey) + writeInt(h, kexDHInit.X) + writeInt(h, Y) + K = make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H = h.Sum() + + h.Reset() + h.Write(H) + hh := h.Sum() + + var sig []byte + switch hostKeyAlgo { + case hostAlgoRSA: + sig, err = rsa.SignPKCS1v15(rand.Reader, s.Server.rsa, hashFunc, hh) + if err != nil { + return + } + default: + return nil, nil, os.NewError("internal error") + } + + serializedSig := serializeRSASignature(sig) + + kexDHReply := kexDHReplyMsg{ + HostKey: serializedHostKey, + Y: Y, + Signature: serializedSig, + } + packet = marshal(msgKexDHReply, kexDHReply) + + err = s.writePacket(packet) + return +} + +func serializeRSASignature(sig []byte) []byte { + length := stringLength([]byte(hostAlgoRSA)) + length += stringLength(sig) + + ret := make([]byte, length) + r := marshalString(ret, []byte(hostAlgoRSA)) + r = marshalString(r, sig) + + return ret +} + +// serverVersion is the fixed identification string that Server will use. +var serverVersion = []byte("SSH-2.0-Go\r\n") + +// buildDataSignedForAuth returns the data that is signed in order to prove +// posession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + user := []byte(req.User) + service := []byte(req.Service) + method := []byte(req.Method) + + length := stringLength(sessionId) + length += 1 + length += stringLength(user) + length += stringLength(service) + length += stringLength(method) + length += 1 + length += stringLength(algo) + length += stringLength(pubKey) + + ret := make([]byte, length) + r := marshalString(ret, sessionId) + r[0] = msgUserAuthRequest + r = r[1:] + r = marshalString(r, user) + r = marshalString(r, service) + r = marshalString(r, method) + r[0] = 1 + r = r[1:] + r = marshalString(r, algo) + r = marshalString(r, pubKey) + return ret +} + +// Handshake performs an SSH transport and client authentication on the given ServerConnection. +func (s *ServerConnection) Handshake(conn net.Conn) os.Error { + var magics handshakeMagics + s.transport = &transport{ + reader: reader{ + Reader: bufio.NewReader(conn), + }, + writer: writer{ + Writer: bufio.NewWriter(conn), + rand: rand.Reader, + }, + Close: func() os.Error { + return conn.Close() + }, + } + + if _, err := conn.Write(serverVersion); err != nil { + return err + } + magics.serverVersion = serverVersion[:len(serverVersion)-2] + + version, ok := readVersion(s.transport) + if !ok { + return os.NewError("failed to read version string from client") + } + magics.clientVersion = version + + serverKexInit := kexInitMsg{ + KexAlgos: supportedKexAlgos, + ServerHostKeyAlgos: supportedHostKeyAlgos, + CiphersClientServer: supportedCiphers, + CiphersServerClient: supportedCiphers, + MACsClientServer: supportedMACs, + MACsServerClient: supportedMACs, + CompressionClientServer: supportedCompressions, + CompressionServerClient: supportedCompressions, + } + kexInitPacket := marshal(msgKexInit, serverKexInit) + magics.serverKexInit = kexInitPacket + + if err := s.writePacket(kexInitPacket); err != nil { + return err + } + + packet, err := s.readPacket() + if err != nil { + return err + } + + magics.clientKexInit = packet + + var clientKexInit kexInitMsg + if err = unmarshal(&clientKexInit, packet, msgKexInit); err != nil { + return err + } + + kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, s.transport, &clientKexInit, &serverKexInit) + if !ok { + return os.NewError("ssh: no common algorithms") + } + + if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0] { + // The client sent a Kex message for the wrong algorithm, + // which we have to ignore. + _, err := s.readPacket() + if err != nil { + return err + } + } + + var H, K []byte + var hashFunc crypto.Hash + switch kexAlgo { + case kexAlgoDH14SHA1: + hashFunc = crypto.SHA1 + dhGroup14Once.Do(initDHGroup14) + H, K, err = s.kexDH(dhGroup14, hashFunc, &magics, hostKeyAlgo) + default: + err = os.NewError("ssh: internal error") + } + + if err != nil { + return err + } + + packet = []byte{msgNewKeys} + if err = s.writePacket(packet); err != nil { + return err + } + if err = s.transport.writer.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { + return err + } + + if packet, err = s.readPacket(); err != nil { + return err + } + if packet[0] != msgNewKeys { + return UnexpectedMessageError{msgNewKeys, packet[0]} + } + + s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc) + + packet, err = s.readPacket() + if err != nil { + return err + } + + var serviceRequest serviceRequestMsg + if err = unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil { + return err + } + if serviceRequest.Service != serviceUserAuth { + return os.NewError("ssh: requested service '" + serviceRequest.Service + "' before authenticating") + } + + serviceAccept := serviceAcceptMsg{ + Service: serviceUserAuth, + } + packet = marshal(msgServiceAccept, serviceAccept) + if err = s.writePacket(packet); err != nil { + return err + } + + if err = s.authenticate(H); err != nil { + return err + } + + s.channels = make(map[uint32]*channel) + return nil +} + +func isAcceptableAlgo(algo string) bool { + return algo == hostAlgoRSA +} + +// testPubKey returns true if the given public key is acceptable for the user. +func (s *ServerConnection) testPubKey(user, algo string, pubKey []byte) bool { + if s.Server.PubKeyCallback == nil || !isAcceptableAlgo(algo) { + return false + } + + for _, c := range s.cachedPubKeys { + if c.user == user && c.algo == algo && bytes.Equal(c.pubKey, pubKey) { + return c.result + } + } + + result := s.Server.PubKeyCallback(user, algo, pubKey) + if len(s.cachedPubKeys) < maxCachedPubKeys { + c := cachedPubKey{ + user: user, + algo: algo, + pubKey: make([]byte, len(pubKey)), + result: result, + } + copy(c.pubKey, pubKey) + s.cachedPubKeys = append(s.cachedPubKeys, c) + } + + return result +} + +func (s *ServerConnection) authenticate(H []byte) os.Error { + var userAuthReq userAuthRequestMsg + var err os.Error + var packet []byte + +userAuthLoop: + for { + if packet, err = s.readPacket(); err != nil { + return err + } + if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil { + return err + } + + if userAuthReq.Service != serviceSSH { + return os.NewError("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + } + + switch userAuthReq.Method { + case "none": + if s.Server.NoClientAuth { + break userAuthLoop + } + case "password": + if s.Server.PasswordCallback == nil { + break + } + payload := userAuthReq.Payload + if len(payload) < 1 || payload[0] != 0 { + return ParseError{msgUserAuthRequest} + } + payload = payload[1:] + password, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + + if s.Server.PasswordCallback(userAuthReq.User, string(password)) { + break userAuthLoop + } + case "publickey": + if s.Server.PubKeyCallback == nil { + break + } + payload := userAuthReq.Payload + if len(payload) < 1 { + return ParseError{msgUserAuthRequest} + } + isQuery := payload[0] == 0 + payload = payload[1:] + algoBytes, payload, ok := parseString(payload) + if !ok { + return ParseError{msgUserAuthRequest} + } + algo := string(algoBytes) + + pubKey, payload, ok := parseString(payload) + if !ok { + return ParseError{msgUserAuthRequest} + } + if isQuery { + // The client can query if the given public key + // would be ok. + if len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + if s.testPubKey(userAuthReq.User, algo, pubKey) { + okMsg := userAuthPubKeyOkMsg{ + Algo: algo, + PubKey: string(pubKey), + } + if err = s.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil { + return err + } + continue userAuthLoop + } + } else { + sig, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return ParseError{msgUserAuthRequest} + } + if !isAcceptableAlgo(algo) { + break + } + rsaSig, ok := parseRSASig(sig) + if !ok { + return ParseError{msgUserAuthRequest} + } + signedData := buildDataSignedForAuth(H, userAuthReq, algoBytes, pubKey) + switch algo { + case hostAlgoRSA: + hashFunc := crypto.SHA1 + h := hashFunc.New() + h.Write(signedData) + digest := h.Sum() + rsaKey, ok := parseRSA(pubKey) + if !ok { + return ParseError{msgUserAuthRequest} + } + if rsa.VerifyPKCS1v15(rsaKey, hashFunc, digest, rsaSig) != nil { + return ParseError{msgUserAuthRequest} + } + default: + return os.NewError("ssh: isAcceptableAlgo incorrect") + } + if s.testPubKey(userAuthReq.User, algo, pubKey) { + break userAuthLoop + } + } + } + + var failureMsg userAuthFailureMsg + if s.Server.PasswordCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "password") + } + if s.Server.PubKeyCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "publickey") + } + + if len(failureMsg.Methods) == 0 { + return os.NewError("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if err = s.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil { + return err + } + } + + packet = []byte{msgUserAuthSuccess} + if err = s.writePacket(packet); err != nil { + return err + } + + return nil +} + +const defaultWindowSize = 32768 + +// Accept reads and processes messages on a ServerConnection. It must be called +// in order to demultiplex messages to any resulting Channels. +func (s *ServerConnection) Accept() (Channel, os.Error) { + if s.err != nil { + return nil, s.err + } + + for { + packet, err := s.readPacket() + if err != nil { + + s.lock.Lock() + s.err = err + s.lock.Unlock() + + for _, c := range s.channels { + c.dead = true + c.handleData(nil) + } + + return nil, err + } + + switch packet[0] { + case msgChannelOpen: + var chanOpen channelOpenMsg + if err := unmarshal(&chanOpen, packet, msgChannelOpen); err != nil { + return nil, err + } + + c := new(channel) + c.chanType = chanOpen.ChanType + c.theirId = chanOpen.PeersId + c.theirWindow = chanOpen.PeersWindow + c.maxPacketSize = chanOpen.MaxPacketSize + c.extraData = chanOpen.TypeSpecificData + c.myWindow = defaultWindowSize + c.serverConn = s + c.cond = sync.NewCond(&c.lock) + c.pendingData = make([]byte, c.myWindow) + + s.lock.Lock() + c.myId = s.nextChanId + s.nextChanId++ + s.channels[c.myId] = c + s.lock.Unlock() + return c, nil + + case msgChannelRequest: + var chanRequest channelRequestMsg + if err := unmarshal(&chanRequest, packet, msgChannelRequest); err != nil { + return nil, err + } + + s.lock.Lock() + c, ok := s.channels[chanRequest.PeersId] + if !ok { + continue + } + c.handlePacket(&chanRequest) + s.lock.Unlock() + + case msgChannelData: + if len(packet) < 5 { + return nil, ParseError{msgChannelData} + } + chanId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4]) + + s.lock.Lock() + c, ok := s.channels[chanId] + if !ok { + continue + } + c.handleData(packet[9:]) + s.lock.Unlock() + + case msgChannelEOF: + var eofMsg channelEOFMsg + if err := unmarshal(&eofMsg, packet, msgChannelEOF); err != nil { + return nil, err + } + + s.lock.Lock() + c, ok := s.channels[eofMsg.PeersId] + if !ok { + continue + } + c.handlePacket(&eofMsg) + s.lock.Unlock() + + case msgChannelClose: + var closeMsg channelCloseMsg + if err := unmarshal(&closeMsg, packet, msgChannelClose); err != nil { + return nil, err + } + + s.lock.Lock() + c, ok := s.channels[closeMsg.PeersId] + if !ok { + continue + } + c.handlePacket(&closeMsg) + s.lock.Unlock() + + case msgGlobalRequest: + var request globalRequestMsg + if err := unmarshal(&request, packet, msgGlobalRequest); err != nil { + return nil, err + } + + if request.WantReply { + if err := s.writePacket([]byte{msgRequestFailure}); err != nil { + return nil, err + } + } + + default: + // Unknown message. Ignore. + } + } + + panic("unreachable") +} diff --git a/src/pkg/exp/ssh/server_shell.go b/src/pkg/exp/ssh/server_shell.go new file mode 100644 index 000000000..53a3241f5 --- /dev/null +++ b/src/pkg/exp/ssh/server_shell.go @@ -0,0 +1,399 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "os" +) + +// ServerShell contains the state for running a VT100 terminal that is capable +// of reading lines of input. +type ServerShell struct { + c Channel + prompt string + + // line is the current line being entered. + line []byte + // pos is the logical position of the cursor in line + pos int + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte +} + +// NewServerShell runs a VT100 terminal on the given channel. prompt is a +// string that is written at the start of each input line. For example: "> ". +func NewServerShell(c Channel, prompt string) *ServerShell { + return &ServerShell{ + c: c, + prompt: prompt, + termWidth: 80, + termHeight: 24, + } +} + +const ( + keyCtrlD = 4 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 256 + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns -1. +func bytesToKey(b []byte) (int, []byte) { + if len(b) == 0 { + return -1, nil + } + + if b[0] != keyEscape { + return int(b[0]), b[1:] + } + + if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + } + } + + if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { + return keyUnknown, b[i+1:] + } + } + + return -1, b +} + +// queue appends data to the end of ss.outBuf +func (ss *ServerShell) queue(data []byte) { + if len(ss.outBuf)+len(data) > cap(ss.outBuf) { + newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data))) + copy(newOutBuf, ss.outBuf) + ss.outBuf = newOutBuf + } + + oldLen := len(ss.outBuf) + ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)] + copy(ss.outBuf[oldLen:], data) +} + +var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} + +func isPrintable(key int) bool { + return key >= 32 && key < 127 +} + +// moveCursorToPos appends data to ss.outBuf which will move the cursor to the +// given, logical position in the text. +func (ss *ServerShell) moveCursorToPos(pos int) { + x := len(ss.prompt) + pos + y := x / ss.termWidth + x = x % ss.termWidth + + up := 0 + if y < ss.cursorY { + up = ss.cursorY - y + } + + down := 0 + if y > ss.cursorY { + down = y - ss.cursorY + } + + left := 0 + if x < ss.cursorX { + left = ss.cursorX - x + } + + right := 0 + if x > ss.cursorX { + right = x - ss.cursorX + } + + movement := make([]byte, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + ss.cursorX = x + ss.cursorY = y + ss.queue(movement) +} + +const maxLineLength = 4096 + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (ss *ServerShell) handleKey(key int) (line string, ok bool) { + switch key { + case keyBackspace: + if ss.pos == 0 { + return + } + ss.pos-- + + copy(ss.line[ss.pos:], ss.line[1+ss.pos:]) + ss.line = ss.line[:len(ss.line)-1] + ss.writeLine(ss.line[ss.pos:]) + ss.moveCursorToPos(ss.pos) + ss.queue(eraseUnderCursor) + case keyAltLeft: + // move left by a word. + if ss.pos == 0 { + return + } + ss.pos-- + for ss.pos > 0 { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos-- + } + for ss.pos > 0 { + if ss.line[ss.pos] == ' ' { + ss.pos++ + break + } + ss.pos-- + } + ss.moveCursorToPos(ss.pos) + case keyAltRight: + // move right by a word. + for ss.pos < len(ss.line) { + if ss.line[ss.pos] == ' ' { + break + } + ss.pos++ + } + for ss.pos < len(ss.line) { + if ss.line[ss.pos] != ' ' { + break + } + ss.pos++ + } + ss.moveCursorToPos(ss.pos) + case keyLeft: + if ss.pos == 0 { + return + } + ss.pos-- + ss.moveCursorToPos(ss.pos) + case keyRight: + if ss.pos == len(ss.line) { + return + } + ss.pos++ + ss.moveCursorToPos(ss.pos) + case keyEnter: + ss.moveCursorToPos(len(ss.line)) + ss.queue([]byte("\r\n")) + line = string(ss.line) + ok = true + ss.line = ss.line[:0] + ss.pos = 0 + ss.cursorX = 0 + ss.cursorY = 0 + ss.maxLine = 0 + default: + if !isPrintable(key) { + return + } + if len(ss.line) == maxLineLength { + return + } + if len(ss.line) == cap(ss.line) { + newLine := make([]byte, len(ss.line), 2*(1+len(ss.line))) + copy(newLine, ss.line) + ss.line = newLine + } + ss.line = ss.line[:len(ss.line)+1] + copy(ss.line[ss.pos+1:], ss.line[ss.pos:]) + ss.line[ss.pos] = byte(key) + ss.writeLine(ss.line[ss.pos:]) + ss.pos++ + ss.moveCursorToPos(ss.pos) + } + return +} + +func (ss *ServerShell) writeLine(line []byte) { + for len(line) != 0 { + if ss.cursorX == ss.termWidth { + ss.queue([]byte("\r\n")) + ss.cursorX = 0 + ss.cursorY++ + if ss.cursorY > ss.maxLine { + ss.maxLine = ss.cursorY + } + } + + remainingOnLine := ss.termWidth - ss.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + ss.queue(line[:todo]) + ss.cursorX += todo + line = line[todo:] + } +} + +// parsePtyRequest parses the payload of the pty-req message and extracts the +// dimensions of the terminal. See RFC 4254, section 6.2. +func parsePtyRequest(s []byte) (width, height int, ok bool) { + _, s, ok = parseString(s) + if !ok { + return + } + width32, s, ok := parseUint32(s) + if !ok { + return + } + height32, _, ok := parseUint32(s) + width = int(width32) + height = int(height32) + if width < 1 { + ok = false + } + if height < 1 { + ok = false + } + return +} + +func (ss *ServerShell) Write(buf []byte) (n int, err os.Error) { + return ss.c.Write(buf) +} + +// ReadLine returns a line of input from the terminal. +func (ss *ServerShell) ReadLine() (line string, err os.Error) { + ss.writeLine([]byte(ss.prompt)) + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + + for { + // ss.remainder is a slice at the beginning of ss.inBuf + // containing a partial key sequence + readBuf := ss.inBuf[len(ss.remainder):] + n, err := ss.c.Read(readBuf) + if err == nil { + ss.remainder = ss.inBuf[:n+len(ss.remainder)] + rest := ss.remainder + lineOk := false + for !lineOk { + var key int + key, rest = bytesToKey(rest) + if key < 0 { + break + } + if key == keyCtrlD { + return "", os.EOF + } + line, lineOk = ss.handleKey(key) + } + if len(rest) > 0 { + n := copy(ss.inBuf[:], rest) + ss.remainder = ss.inBuf[:n] + } else { + ss.remainder = nil + } + ss.c.Write(ss.outBuf) + ss.outBuf = ss.outBuf[:0] + if lineOk { + return + } + continue + } + + if req, ok := err.(ChannelRequest); ok { + ok := false + switch req.Request { + case "pty-req": + ss.termWidth, ss.termHeight, ok = parsePtyRequest(req.Payload) + if !ok { + ss.termWidth = 80 + ss.termHeight = 24 + } + case "shell": + ok = true + if len(req.Payload) > 0 { + // We don't accept any commands, only the default shell. + ok = false + } + case "env": + ok = true + } + if req.WantReply { + ss.c.AckRequest(ok) + } + } else { + return "", err + } + } + panic("unreachable") +} diff --git a/src/pkg/exp/ssh/server_shell_test.go b/src/pkg/exp/ssh/server_shell_test.go new file mode 100644 index 000000000..622cf7cfa --- /dev/null +++ b/src/pkg/exp/ssh/server_shell_test.go @@ -0,0 +1,134 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "testing" + "os" +) + +type MockChannel struct { + toSend []byte + bytesPerRead int + received []byte +} + +func (c *MockChannel) Accept() os.Error { + return nil +} + +func (c *MockChannel) Reject(RejectionReason, string) os.Error { + return nil +} + +func (c *MockChannel) Read(data []byte) (n int, err os.Error) { + n = len(data) + if n == 0 { + return + } + if n > len(c.toSend) { + n = len(c.toSend) + } + if n == 0 { + return 0, os.EOF + } + if c.bytesPerRead > 0 && n > c.bytesPerRead { + n = c.bytesPerRead + } + copy(data, c.toSend[:n]) + c.toSend = c.toSend[n:] + return +} + +func (c *MockChannel) Write(data []byte) (n int, err os.Error) { + c.received = append(c.received, data...) + return len(data), nil +} + +func (c *MockChannel) Close() os.Error { + return nil +} + +func (c *MockChannel) AckRequest(ok bool) os.Error { + return nil +} + +func (c *MockChannel) ChannelType() string { + return "" +} + +func (c *MockChannel) ExtraData() []byte { + return nil +} + +func TestClose(t *testing.T) { + c := &MockChannel{} + ss := NewServerShell(c, "> ") + line, err := ss.ReadLine() + if line != "" { + t.Errorf("Expected empty line but got: %s", line) + } + if err != os.EOF { + t.Errorf("Error should have been EOF but got: %s", err) + } +} + +var keyPressTests = []struct { + in string + line string + err os.Error +}{ + { + "", + "", + os.EOF, + }, + { + "\r", + "", + nil, + }, + { + "foo\r", + "foo", + nil, + }, + { + "a\x1b[Cb\r", // right + "ab", + nil, + }, + { + "a\x1b[Db\r", // left + "ba", + nil, + }, + { + "a\177b\r", // backspace + "b", + nil, + }, +} + +func TestKeyPresses(t *testing.T) { + for i, test := range keyPressTests { + for j := 0; j < len(test.in); j++ { + c := &MockChannel{ + toSend: []byte(test.in), + bytesPerRead: j, + } + ss := NewServerShell(c, "> ") + line, err := ss.ReadLine() + if line != test.line { + t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) + break + } + if err != test.err { + t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) + break + } + } + } +} diff --git a/src/pkg/exp/ssh/transport.go b/src/pkg/exp/ssh/transport.go new file mode 100644 index 000000000..5a474a9f2 --- /dev/null +++ b/src/pkg/exp/ssh/transport.go @@ -0,0 +1,322 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bufio" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/subtle" + "hash" + "io" + "os" +) + +const ( + paddingMultiple = 16 // TODO(dfc) does this need to be configurable? +) + +// transport represents the SSH connection to the remote peer. +type transport struct { + reader + writer + + cipherAlgo string + macAlgo string + compressionAlgo string + + Close func() os.Error +} + +// reader represents the incoming connection state. +type reader struct { + io.Reader + common +} + +// writer represnts the outgoing connection state. +type writer struct { + *bufio.Writer + paddingMultiple int + rand io.Reader + common +} + +// common represents the cipher state needed to process messages in a single +// direction. +type common struct { + seqNum uint32 + mac hash.Hash + cipher cipher.Stream +} + +// Read and decrypt a single packet from the remote peer. +func (r *reader) readOnePacket() ([]byte, os.Error) { + var lengthBytes = make([]byte, 5) + var macSize uint32 + + if _, err := io.ReadFull(r, lengthBytes); err != nil { + return nil, err + } + + if r.cipher != nil { + r.cipher.XORKeyStream(lengthBytes, lengthBytes) + } + + if r.mac != nil { + r.mac.Reset() + seqNumBytes := []byte{ + byte(r.seqNum >> 24), + byte(r.seqNum >> 16), + byte(r.seqNum >> 8), + byte(r.seqNum), + } + r.mac.Write(seqNumBytes) + r.mac.Write(lengthBytes) + macSize = uint32(r.mac.Size()) + } + + length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) + paddingLength := uint32(lengthBytes[4]) + + if length <= paddingLength+1 { + return nil, os.NewError("invalid packet length") + } + if length > maxPacketSize { + return nil, os.NewError("packet too large") + } + + packet := make([]byte, length-1+macSize) + if _, err := io.ReadFull(r, packet); err != nil { + return nil, err + } + mac := packet[length-1:] + if r.cipher != nil { + r.cipher.XORKeyStream(packet, packet[:length-1]) + } + + if r.mac != nil { + r.mac.Write(packet[:length-1]) + if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 { + return nil, os.NewError("ssh: MAC failure") + } + } + + r.seqNum++ + return packet[:length-paddingLength-1], nil +} + +// Read and decrypt next packet discarding debug and noop messages. +func (t *transport) readPacket() ([]byte, os.Error) { + for { + packet, err := t.readOnePacket() + if err != nil { + return nil, err + } + if packet[0] != msgIgnore && packet[0] != msgDebug { + return packet, nil + } + } + panic("unreachable") +} + +// Encrypt and send a packet of data to the remote peer. +func (w *writer) writePacket(packet []byte) os.Error { + paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple + if paddingLength < 4 { + paddingLength += paddingMultiple + } + + length := len(packet) + 1 + paddingLength + lengthBytes := []byte{ + byte(length >> 24), + byte(length >> 16), + byte(length >> 8), + byte(length), + byte(paddingLength), + } + padding := make([]byte, paddingLength) + _, err := io.ReadFull(w.rand, padding) + if err != nil { + return err + } + + if w.mac != nil { + w.mac.Reset() + seqNumBytes := []byte{ + byte(w.seqNum >> 24), + byte(w.seqNum >> 16), + byte(w.seqNum >> 8), + byte(w.seqNum), + } + w.mac.Write(seqNumBytes) + w.mac.Write(lengthBytes) + w.mac.Write(packet) + w.mac.Write(padding) + } + + // TODO(dfc) lengthBytes, packet and padding should be + // subslices of a single buffer + if w.cipher != nil { + w.cipher.XORKeyStream(lengthBytes, lengthBytes) + w.cipher.XORKeyStream(packet, packet) + w.cipher.XORKeyStream(padding, padding) + } + + if _, err := w.Write(lengthBytes); err != nil { + return err + } + if _, err := w.Write(packet); err != nil { + return err + } + if _, err := w.Write(padding); err != nil { + return err + } + + if w.mac != nil { + if _, err := w.Write(w.mac.Sum()); err != nil { + return err + } + } + + if err := w.Flush(); err != nil { + return err + } + w.seqNum++ + return err +} + +type direction struct { + ivTag []byte + keyTag []byte + macKeyTag []byte +} + +// TODO(dfc) can this be made a constant ? +var ( + serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} + clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} +) + +// setupKeys sets the cipher and MAC keys from K, H and sessionId, as +// described in RFC 4253, section 6.4. direction should either be serverKeys +// (to setup server->client keys) or clientKeys (for client->server keys). +func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) os.Error { + h := hashFunc.New() + + blockSize := 16 + keySize := 16 + macKeySize := 20 + + iv := make([]byte, blockSize) + key := make([]byte, keySize) + macKey := make([]byte, macKeySize) + generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) + generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) + generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) + + c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} + aes, err := aes.NewCipher(key) + if err != nil { + return err + } + c.cipher = cipher.NewCTR(aes, iv) + return nil +} + +// generateKeyMaterial fills out with key material generated from tag, K, H +// and sessionId, as specified in RFC 4253, section 7.2. +func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { + var digestsSoFar []byte + + for len(out) > 0 { + h.Reset() + h.Write(K) + h.Write(H) + + if len(digestsSoFar) == 0 { + h.Write(tag) + h.Write(sessionId) + } else { + h.Write(digestsSoFar) + } + + digest := h.Sum() + n := copy(out, digest) + out = out[n:] + if len(out) > 0 { + digestsSoFar = append(digestsSoFar, digest...) + } + } +} + +// truncatingMAC wraps around a hash.Hash and truncates the output digest to +// a given size. +type truncatingMAC struct { + length int + hmac hash.Hash +} + +func (t truncatingMAC) Write(data []byte) (int, os.Error) { + return t.hmac.Write(data) +} + +func (t truncatingMAC) Sum() []byte { + digest := t.hmac.Sum() + return digest[:t.length] +} + +func (t truncatingMAC) Reset() { + t.hmac.Reset() +} + +func (t truncatingMAC) Size() int { + return t.length +} + +// maxVersionStringBytes is the maximum number of bytes that we'll accept as a +// version string. In the event that the client is talking a different protocol +// we need to set a limit otherwise we will keep using more and more memory +// while searching for the end of the version handshake. +const maxVersionStringBytes = 1024 + +// Read version string as specified by RFC 4253, section 4.2. +func readVersion(r io.Reader) (versionString []byte, ok bool) { + versionString = make([]byte, 0, 64) + seenCR := false + + var buf [1]byte +forEachByte: + for len(versionString) < maxVersionStringBytes { + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return + } + b := buf[0] + + if !seenCR { + if b == '\r' { + seenCR = true + } + } else { + if b == '\n' { + ok = true + break forEachByte + } else { + seenCR = false + } + } + versionString = append(versionString, b) + } + + if ok { + // We need to remove the CR from versionString + versionString = versionString[:len(versionString)-1] + } + + return +} diff --git a/src/pkg/exp/ssh/transport_test.go b/src/pkg/exp/ssh/transport_test.go new file mode 100644 index 000000000..9a610a780 --- /dev/null +++ b/src/pkg/exp/ssh/transport_test.go @@ -0,0 +1,37 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bufio" + "bytes" + "testing" +) + +func TestReadVersion(t *testing.T) { + buf := []byte(serverVersion) + result, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) + if !ok { + t.Error("readVersion didn't read version correctly") + } + if !bytes.Equal(buf[:len(buf)-2], result) { + t.Error("version read did not match expected") + } +} + +func TestReadVersionTooLong(t *testing.T) { + buf := make([]byte, maxVersionStringBytes+1) + if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok { + t.Errorf("readVersion consumed %d bytes without error", len(buf)) + } +} + +func TestReadVersionWithoutCRLF(t *testing.T) { + buf := []byte(serverVersion) + buf = buf[:len(buf)-1] + if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok { + t.Error("readVersion did not notice \\n was missing") + } +} diff --git a/src/pkg/exp/template/html/Makefile b/src/pkg/exp/template/html/Makefile index 3a93bebc0..9032aead8 100644 --- a/src/pkg/exp/template/html/Makefile +++ b/src/pkg/exp/template/html/Makefile @@ -6,8 +6,16 @@ include ../../../../Make.inc TARG=exp/template/html GOFILES=\ + clone.go\ + content.go\ context.go\ + css.go\ + doc.go\ + error.go\ escape.go\ + html.go\ js.go\ + transition.go\ + url.go\ include ../../../../Make.pkg diff --git a/src/pkg/exp/template/html/clone.go b/src/pkg/exp/template/html/clone.go new file mode 100644 index 000000000..803a64de1 --- /dev/null +++ b/src/pkg/exp/template/html/clone.go @@ -0,0 +1,90 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "template/parse" +) + +// clone clones a template Node. +func clone(n parse.Node) parse.Node { + switch t := n.(type) { + case *parse.ActionNode: + return cloneAction(t) + case *parse.IfNode: + b := new(parse.IfNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + case *parse.ListNode: + return cloneList(t) + case *parse.RangeNode: + b := new(parse.RangeNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + case *parse.TemplateNode: + return cloneTemplate(t) + case *parse.TextNode: + return cloneText(t) + case *parse.WithNode: + b := new(parse.WithNode) + copyBranch(&b.BranchNode, &t.BranchNode) + return b + } + panic("cloning " + n.String() + " is unimplemented") +} + +// cloneAction returns a deep clone of n. +func cloneAction(n *parse.ActionNode) *parse.ActionNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.ActionNode{n.NodeType, n.Line, clonePipe(n.Pipe)} +} + +// cloneList returns a deep clone of n. +func cloneList(n *parse.ListNode) *parse.ListNode { + if n == nil { + return nil + } + // We use keyless fields because they won't compile if a field is added. + c := parse.ListNode{n.NodeType, make([]parse.Node, len(n.Nodes))} + for i, child := range n.Nodes { + c.Nodes[i] = clone(child) + } + return &c +} + +// clonePipe returns a shallow clone of n. +// The escaper does not modify pipe descendants in place so there's no need to +// clone deeply. +func clonePipe(n *parse.PipeNode) *parse.PipeNode { + if n == nil { + return nil + } + // We use keyless fields because they won't compile if a field is added. + return &parse.PipeNode{n.NodeType, n.Line, n.Decl, n.Cmds} +} + +// cloneTemplate returns a deep clone of n. +func cloneTemplate(n *parse.TemplateNode) *parse.TemplateNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.TemplateNode{n.NodeType, n.Line, n.Name, clonePipe(n.Pipe)} +} + +// cloneText clones the given node sharing its []byte. +func cloneText(n *parse.TextNode) *parse.TextNode { + // We use keyless fields because they won't compile if a field is added. + return &parse.TextNode{n.NodeType, n.Text} +} + +// copyBranch clones src into dst. +func copyBranch(dst, src *parse.BranchNode) { + // We use keyless fields because they won't compile if a field is added. + *dst = parse.BranchNode{ + src.NodeType, + src.Line, + clonePipe(src.Pipe), + cloneList(src.List), + cloneList(src.ElseList), + } +} diff --git a/src/pkg/exp/template/html/clone_test.go b/src/pkg/exp/template/html/clone_test.go new file mode 100644 index 000000000..d91542529 --- /dev/null +++ b/src/pkg/exp/template/html/clone_test.go @@ -0,0 +1,90 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "template" + "template/parse" + "testing" +) + +func TestClone(t *testing.T) { + tests := []struct { + input, want, wantClone string + }{ + { + `Hello, {{if true}}{{"<World>"}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello, {{with "<World>"}}{{.}}{{end}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `{{range .}}<p>{{.}}</p>{{end}}`, + "<p>foo</p><p><bar></p><p>baz</p>", + "<p>foo</p><p><bar></p><p>baz</p>", + }, + { + `Hello, {{"<World>" | html}}!`, + "Hello, <World>!", + "Hello, <World>!", + }, + { + `Hello{{if 1}}, World{{else}}{{template "d"}}{{end}}!`, + "Hello, World!", + "Hello, World!", + }, + } + + for _, test := range tests { + s := template.Must(template.New("s").Parse(test.input)) + d := template.New("d") + d.Tree = &parse.Tree{Name: d.Name(), Root: cloneList(s.Root)} + + if want, got := s.Root.String(), d.Root.String(); want != got { + t.Errorf("want %q, got %q", want, got) + } + + d, err := Escape(d) + if err != nil { + t.Errorf("%q: failed to escape: %s", test.input, err) + continue + } + + if want, got := "s", s.Name(); want != got { + t.Errorf("want %q, got %q", want, got) + continue + } + if want, got := "d", d.Name(); want != got { + t.Errorf("want %q, got %q", want, got) + continue + } + + data := []string{"foo", "<bar>", "baz"} + + // Make sure escaping d did not affect s. + var b bytes.Buffer + s.Execute(&b, data) + if got := b.String(); got != test.want { + t.Errorf("%q: want %q, got %q", test.input, test.want, got) + continue + } + + b.Reset() + d.Execute(&b, data) + if got := b.String(); got != test.wantClone { + t.Errorf("%q: want %q, got %q", test.input, test.wantClone, got) + } + } +} diff --git a/src/pkg/exp/template/html/content.go b/src/pkg/exp/template/html/content.go new file mode 100644 index 000000000..8b9809b98 --- /dev/null +++ b/src/pkg/exp/template/html/content.go @@ -0,0 +1,90 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "fmt" +) + +// Strings of content from a trusted source. +type ( + // CSS encapsulates known safe content that matches any of: + // (1) The CSS3 stylesheet production, such as `p { color: purple }`. + // (2) The CSS3 rule production, such as `a[href=~"https:"].foo#bar`. + // (3) CSS3 declaration productions, such as `color: red; margin: 2px`. + // (4) The CSS3 value production, such as `rgba(0, 0, 255, 127)`. + // See http://www.w3.org/TR/css3-syntax/#style + CSS string + + // HTML encapsulates a known safe HTML document fragment. + // It should not be used for HTML from a third-party, or HTML with + // unclosed tags or comments. The outputs of a sound HTML sanitizer + // and a template escaped by this package are fine for use with HTML. + HTML string + + // HTMLAttr encapsulates an HTML attribute from a trusted source, + // for example: ` dir="ltr"`. + HTMLAttr string + + // JS encapsulates a known safe EcmaScript5 Expression, or example, + // `(x + y * z())`. + // Template authors are responsible for ensuring that typed expressions + // do not break the intended precedence and that there is no + // statement/expression ambiguity as when passing an expression like + // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a + // valid Program with a very different meaning. + JS string + + // JSStr encapsulates a sequence of characters meant to be embedded + // between quotes in a JavaScript expression. + // The string must match a series of StringCharacters: + // StringCharacter :: SourceCharacter but not `\` or LineTerminator + // | EscapeSequence + // Note that LineContinuations are not allowed. + // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. + JSStr string + + // URL encapsulates a known safe URL as defined in RFC 3896. + // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()` + // from a trusted source should go in the page, but by default dynamic + // `javascript:` URLs are filtered out since they are a frequently + // exploited injection vector. + URL string +) + +type contentType uint8 + +const ( + contentTypePlain contentType = iota + contentTypeCSS + contentTypeHTML + contentTypeHTMLAttr + contentTypeJS + contentTypeJSStr + contentTypeURL +) + +// stringify converts its arguments to a string and the type of the content. +func stringify(args ...interface{}) (string, contentType) { + if len(args) == 1 { + switch s := args[0].(type) { + case string: + return s, contentTypePlain + case CSS: + return string(s), contentTypeCSS + case HTML: + return string(s), contentTypeHTML + case HTMLAttr: + return string(s), contentTypeHTMLAttr + case JS: + return string(s), contentTypeJS + case JSStr: + return string(s), contentTypeJSStr + case URL: + return string(s), contentTypeURL + } + } + return fmt.Sprint(args...), contentTypePlain +} diff --git a/src/pkg/exp/template/html/content_test.go b/src/pkg/exp/template/html/content_test.go new file mode 100644 index 000000000..033dee174 --- /dev/null +++ b/src/pkg/exp/template/html/content_test.go @@ -0,0 +1,222 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "strings" + "template" + "testing" +) + +func TestTypedContent(t *testing.T) { + data := []interface{}{ + `<b> "foo%" O'Reilly &bar;`, + CSS(`a[href =~ "//example.com"]#foo`), + HTML(`Hello, <b>World</b> &tc!`), + HTMLAttr(` dir="ltr"`), + JS(`c && alert("Hello, World!");`), + JSStr(`Hello, World & O'Reilly\x21`), + URL(`greeting=H%69&addressee=(World)`), + } + + // For each content sensitive escaper, see how it does on + // each of the typed strings above. + tests := []struct { + // A template containing a single {{.}}. + input string + want []string + }{ + { + `<style>{{.}} { color: blue }</style>`, + []string{ + `ZgotmplZ`, + // Allowed but not escaped. + `a[href =~ "//example.com"]#foo`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `<div style="{{.}}">`, + []string{ + `ZgotmplZ`, + // Allowed and HTML escaped. + `a[href =~ "//example.com"]#foo`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `{{.}}`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Not escaped. + `Hello, <b>World</b> &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<a{{.}}>`, + []string{ + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + // Allowed and HTML escaped. + ` dir="ltr"`, + `ZgotmplZ`, + `ZgotmplZ`, + `ZgotmplZ`, + }, + }, + { + `<a title={{.}}>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Tags stripped, spaces escaped, entity not re-escaped. + `Hello, World &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<a title='{{.}}'>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Tags stripped, entity not re-escaped. + `Hello, World &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<textarea>{{.}}</textarea>`, + []string{ + `<b> "foo%" O'Reilly &bar;`, + `a[href =~ "//example.com"]#foo`, + // Angle brackets escaped to prevent injection of close tags, entity not re-escaped. + `Hello, <b>World</b> &tc!`, + ` dir="ltr"`, + `c && alert("Hello, World!");`, + `Hello, World & O'Reilly\x21`, + `greeting=H%69&addressee=(World)`, + }, + }, + { + `<script>alert({{.}})</script>`, + []string{ + `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"a[href =~ \"//example.com\"]#foo"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e &tc!"`, + `" dir=\"ltr\""`, + // Not escaped. + `c && alert("Hello, World!");`, + // Escape sequence not over-escaped. + `"Hello, World & O'Reilly\x21"`, + `"greeting=H%69&addressee=(World)"`, + }, + }, + { + `<button onclick="alert({{.}})">`, + []string{ + `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"a[href =~ \"//example.com\"]#foo"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e &amp;tc!"`, + `" dir=\"ltr\""`, + // Not JS escaped but HTML escaped. + `c && alert("Hello, World!");`, + // Escape sequence not over-escaped. + `"Hello, World & O'Reilly\x21"`, + `"greeting=H%69&addressee=(World)"`, + }, + }, + { + `<script>alert("{{.}}")</script>`, + []string{ + `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, + `a[href =~ \x22\/\/example.com\x22]#foo`, + `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, + ` dir=\x22ltr\x22`, + `c \x26\x26 alert(\x22Hello, World!\x22);`, + // Escape sequence not over-escaped. + `Hello, World \x26 O\x27Reilly\x21`, + `greeting=H%69\x26addressee=(World)`, + }, + }, + { + `<button onclick='alert("{{.}}")'>`, + []string{ + `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, + `a[href =~ \x22\/\/example.com\x22]#foo`, + `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, + ` dir=\x22ltr\x22`, + `c \x26\x26 alert(\x22Hello, World!\x22);`, + // Escape sequence not over-escaped. + `Hello, World \x26 O\x27Reilly\x21`, + `greeting=H%69\x26addressee=(World)`, + }, + }, + { + `<a href="?q={{.}}">`, + []string{ + `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, + `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, + `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, + `%20dir%3d%22ltr%22`, + `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, + `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, + // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done. + `greeting=H%69&addressee=%28World%29`, + }, + }, + { + `<style>body { background: url('?img={{.}}') }</style>`, + []string{ + `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, + `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, + `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, + `%20dir%3d%22ltr%22`, + `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, + `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, + // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done. + `greeting=H%69&addressee=%28World%29`, + }, + }, + } + + for _, test := range tests { + tmpl := template.Must(Escape(template.Must(template.New("x").Parse(test.input)))) + pre := strings.Index(test.input, "{{.}}") + post := len(test.input) - (pre + 5) + var b bytes.Buffer + for i, x := range data { + b.Reset() + if err := tmpl.Execute(&b, x); err != nil { + t.Errorf("%q with %v: %s", test.input, x, err) + continue + } + if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got { + t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got) + continue + } + } + } +} diff --git a/src/pkg/exp/template/html/context.go b/src/pkg/exp/template/html/context.go index 428b3d0b3..de073f134 100644 --- a/src/pkg/exp/template/html/context.go +++ b/src/pkg/exp/template/html/context.go @@ -20,13 +20,46 @@ type context struct { delim delim urlPart urlPart jsCtx jsCtx - errLine int - errStr string + attr attr + element element + err *Error } // eq returns whether two contexts are equal. func (c context) eq(d context) bool { - return c.state == d.state && c.delim == d.delim && c.urlPart == d.urlPart && c.jsCtx == d.jsCtx && c.errLine == d.errLine && c.errStr == d.errStr + return c.state == d.state && + c.delim == d.delim && + c.urlPart == d.urlPart && + c.jsCtx == d.jsCtx && + c.attr == d.attr && + c.element == d.element && + c.err == d.err +} + +// mangle produces an identifier that includes a suffix that distinguishes it +// from template names mangled with different contexts. +func (c context) mangle(templateName string) string { + // The mangled name for the default context is the input templateName. + if c.state == stateText { + return templateName + } + s := templateName + "$htmltemplate_" + c.state.String() + if c.delim != 0 { + s += "_" + c.delim.String() + } + if c.urlPart != 0 { + s += "_" + c.urlPart.String() + } + if c.jsCtx != 0 { + s += "_" + c.jsCtx.String() + } + if c.attr != 0 { + s += "_" + c.attr.String() + } + if c.element != 0 { + s += "_" + c.element.String() + } + return s } // state describes a high-level HTML parser state. @@ -47,6 +80,20 @@ const ( stateText state = iota // stateTag occurs before an HTML attribute or the end of a tag. stateTag + // stateAttrName occurs inside an attribute name. + // It occurs between the ^'s in ` ^name^ = value`. + stateAttrName + // stateAfterName occurs after an attr name has ended but before any + // equals sign. It occurs between the ^'s in ` name^ ^= value`. + stateAfterName + // stateBeforeValue occurs after the equals sign but before the value. + // It occurs between the ^'s in ` name =^ ^value`. + stateBeforeValue + // stateHTMLCmt occurs inside an <!-- HTML comment -->. + stateHTMLCmt + // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>) + // as described at http://dev.w3.org/html5/spec/syntax.html#elements-0 + stateRCDATA // stateAttr occurs inside an HTML attribute whose content is text. stateAttr // stateURL occurs inside an HTML attribute whose content is a URL. @@ -63,30 +110,78 @@ const ( stateJSBlockCmt // stateJSLineCmt occurs inside a JavaScript // line comment. stateJSLineCmt + // stateCSS occurs inside a <style> element or style attribute. + stateCSS + // stateCSSDqStr occurs inside a CSS double quoted string. + stateCSSDqStr + // stateCSSSqStr occurs inside a CSS single quoted string. + stateCSSSqStr + // stateCSSDqURL occurs inside a CSS double quoted url("..."). + stateCSSDqURL + // stateCSSSqURL occurs inside a CSS single quoted url('...'). + stateCSSSqURL + // stateCSSURL occurs inside a CSS unquoted url(...). + stateCSSURL + // stateCSSBlockCmt occurs inside a CSS /* block comment */. + stateCSSBlockCmt + // stateCSSLineCmt occurs inside a CSS // line comment. + stateCSSLineCmt // stateError is an infectious error state outside any valid // HTML/CSS/JS construct. stateError ) var stateNames = [...]string{ - stateText: "stateText", - stateTag: "stateTag", - stateAttr: "stateAttr", - stateURL: "stateURL", - stateJS: "stateJS", - stateJSDqStr: "stateJSDqStr", - stateJSSqStr: "stateJSSqStr", - stateJSRegexp: "stateJSRegexp", - stateJSBlockCmt: "stateJSBlockCmt", - stateJSLineCmt: "stateJSLineCmt", - stateError: "stateError", + stateText: "stateText", + stateTag: "stateTag", + stateAttrName: "stateAttrName", + stateAfterName: "stateAfterName", + stateBeforeValue: "stateBeforeValue", + stateHTMLCmt: "stateHTMLCmt", + stateRCDATA: "stateRCDATA", + stateAttr: "stateAttr", + stateURL: "stateURL", + stateJS: "stateJS", + stateJSDqStr: "stateJSDqStr", + stateJSSqStr: "stateJSSqStr", + stateJSRegexp: "stateJSRegexp", + stateJSBlockCmt: "stateJSBlockCmt", + stateJSLineCmt: "stateJSLineCmt", + stateCSS: "stateCSS", + stateCSSDqStr: "stateCSSDqStr", + stateCSSSqStr: "stateCSSSqStr", + stateCSSDqURL: "stateCSSDqURL", + stateCSSSqURL: "stateCSSSqURL", + stateCSSURL: "stateCSSURL", + stateCSSBlockCmt: "stateCSSBlockCmt", + stateCSSLineCmt: "stateCSSLineCmt", + stateError: "stateError", } func (s state) String() string { if int(s) < len(stateNames) { return stateNames[s] } - return fmt.Sprintf("illegal state %d", s) + return fmt.Sprintf("illegal state %d", int(s)) +} + +// isComment is true for any state that contains content meant for template +// authors & maintainers, not for end-users or machines. +func isComment(s state) bool { + switch s { + case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt: + return true + } + return false +} + +// isInTag return whether s occurs solely inside an HTML tag. +func isInTag(s state) bool { + switch s { + case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr: + return true + } + return false } // delim is the delimiter that will end the current HTML attribute. @@ -115,7 +210,7 @@ func (d delim) String() string { if int(d) < len(delimNames) { return delimNames[d] } - return fmt.Sprintf("illegal delim %d", d) + return fmt.Sprintf("illegal delim %d", int(d)) } // urlPart identifies a part in an RFC 3986 hierarchical URL to allow different @@ -132,8 +227,8 @@ const ( // urlPartQueryOrFrag occurs in the query portion between the ^s in // "http://auth/path?^k=v#frag^". urlPartQueryOrFrag - // urlPartUnknown occurs due to joining of contexts both before and after - // the query separator. + // urlPartUnknown occurs due to joining of contexts both before and + // after the query separator. urlPartUnknown ) @@ -148,7 +243,7 @@ func (u urlPart) String() string { if int(u) < len(urlPartNames) { return urlPartNames[u] } - return fmt.Sprintf("illegal urlPart %d", u) + return fmt.Sprintf("illegal urlPart %d", int(u)) } // jsCtx determines whether a '/' starts a regular expression literal or a @@ -160,6 +255,8 @@ const ( jsCtxRegexp jsCtx = iota // jsCtxDivOp occurs where a '/' would start a division operator. jsCtxDivOp + // jsCtxUnknown occurs where a '/' is ambiguous due to context joining. + jsCtxUnknown ) func (c jsCtx) String() string { @@ -168,6 +265,71 @@ func (c jsCtx) String() string { return "jsCtxRegexp" case jsCtxDivOp: return "jsCtxDivOp" + case jsCtxUnknown: + return "jsCtxUnknown" + } + return fmt.Sprintf("illegal jsCtx %d", int(c)) +} + +// element identifies the HTML element when inside a start tag or special body. +// Certain HTML element (for example <script> and <style>) have bodies that are +// treated differently from stateText so the element type is necessary to +// transition into the correct context at the end of a tag and to identify the +// end delimiter for the body. +type element uint8 + +const ( + // elementNone occurs outside a special tag or special element body. + elementNone element = iota + // elementScript corresponds to the raw text <script> element. + elementScript + // elementStyle corresponds to the raw text <style> element. + elementStyle + // elementTextarea corresponds to the RCDATA <textarea> element. + elementTextarea + // elementTitle corresponds to the RCDATA <title> element. + elementTitle +) + +var elementNames = [...]string{ + elementNone: "elementNone", + elementScript: "elementScript", + elementStyle: "elementStyle", + elementTextarea: "elementTextarea", + elementTitle: "elementTitle", +} + +func (e element) String() string { + if int(e) < len(elementNames) { + return elementNames[e] + } + return fmt.Sprintf("illegal element %d", int(e)) +} + +// attr identifies the most recent HTML attribute when inside a start tag. +type attr uint8 + +const ( + // attrNone corresponds to a normal attribute or no attribute. + attrNone attr = iota + // attrScript corresponds to an event handler attribute. + attrScript + // attrStyle corresponds to the style attribute whose value is CSS. + attrStyle + // attrURL corresponds to an attribute whose value is a URL. + attrURL +) + +var attrNames = [...]string{ + attrNone: "attrNone", + attrScript: "attrScript", + attrStyle: "attrStyle", + attrURL: "attrURL", +} + +func (a attr) String() string { + if int(a) < len(attrNames) { + return attrNames[a] } - return fmt.Sprintf("illegal jsCtx %d", c) + return fmt.Sprintf("illegal attr %d", int(a)) } diff --git a/src/pkg/exp/template/html/css.go b/src/pkg/exp/template/html/css.go new file mode 100644 index 000000000..d881328c9 --- /dev/null +++ b/src/pkg/exp/template/html/css.go @@ -0,0 +1,259 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "fmt" + "unicode" + "utf8" +) + +// endsWithCSSKeyword returns whether b ends with an ident that +// case-insensitively matches the lower-case kw. +func endsWithCSSKeyword(b []byte, kw string) bool { + i := len(b) - len(kw) + if i < 0 { + // Too short. + return false + } + if i != 0 { + r, _ := utf8.DecodeLastRune(b[:i]) + if isCSSNmchar(r) { + // Too long. + return false + } + } + // Many CSS keywords, such as "!important" can have characters encoded, + // but the URI production does not allow that according to + // http://www.w3.org/TR/css3-syntax/#TOK-URI + // This does not attempt to recognize encoded keywords. For example, + // given "\75\72\6c" and "url" this return false. + return string(bytes.ToLower(b[i:])) == kw +} + +// isCSSNmchar returns whether rune is allowed anywhere in a CSS identifier. +func isCSSNmchar(rune int) bool { + // Based on the CSS3 nmchar production but ignores multi-rune escape + // sequences. + // http://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar + return 'a' <= rune && rune <= 'z' || + 'A' <= rune && rune <= 'Z' || + '0' <= rune && rune <= '9' || + '-' == rune || + '_' == rune || + // Non-ASCII cases below. + 0x80 <= rune && rune <= 0xd7ff || + 0xe000 <= rune && rune <= 0xfffd || + 0x10000 <= rune && rune <= 0x10ffff +} + +// decodeCSS decodes CSS3 escapes given a sequence of stringchars. +// If there is no change, it returns the input, otherwise it returns a slice +// backed by a new array. +// http://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar. +func decodeCSS(s []byte) []byte { + i := bytes.IndexByte(s, '\\') + if i == -1 { + return s + } + // The UTF-8 sequence for a codepoint is never longer than 1 + the + // number hex digits need to represent that codepoint, so len(s) is an + // upper bound on the output length. + b := make([]byte, 0, len(s)) + for len(s) != 0 { + i := bytes.IndexByte(s, '\\') + if i == -1 { + i = len(s) + } + b, s = append(b, s[:i]...), s[i:] + if len(s) < 2 { + break + } + // http://www.w3.org/TR/css3-syntax/#SUBTOK-escape + // escape ::= unicode | '\' [#x20-#x7E#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] + if isHex(s[1]) { + // http://www.w3.org/TR/css3-syntax/#SUBTOK-unicode + // unicode ::= '\' [0-9a-fA-F]{1,6} wc? + j := 2 + for j < len(s) && j < 7 && isHex(s[j]) { + j++ + } + rune := hexDecode(s[1:j]) + if rune > unicode.MaxRune { + rune, j = rune/16, j-1 + } + n := utf8.EncodeRune(b[len(b):cap(b)], rune) + // The optional space at the end allows a hex + // sequence to be followed by a literal hex. + // string(decodeCSS([]byte(`\A B`))) == "\nB" + b, s = b[:len(b)+n], skipCSSSpace(s[j:]) + } else { + // `\\` decodes to `\` and `\"` to `"`. + _, n := utf8.DecodeRune(s[1:]) + b, s = append(b, s[1:1+n]...), s[1+n:] + } + } + return b +} + +// isHex returns whether the given character is a hex digit. +func isHex(c byte) bool { + return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' +} + +// hexDecode decodes a short hex digit sequence: "10" -> 16. +func hexDecode(s []byte) int { + n := 0 + for _, c := range s { + n <<= 4 + switch { + case '0' <= c && c <= '9': + n |= int(c - '0') + case 'a' <= c && c <= 'f': + n |= int(c-'a') + 10 + case 'A' <= c && c <= 'F': + n |= int(c-'A') + 10 + default: + panic(fmt.Sprintf("Bad hex digit in %q", s)) + } + } + return n +} + +// skipCSSSpace returns a suffix of c, skipping over a single space. +func skipCSSSpace(c []byte) []byte { + if len(c) == 0 { + return c + } + // wc ::= #x9 | #xA | #xC | #xD | #x20 + switch c[0] { + case '\t', '\n', '\f', ' ': + return c[1:] + case '\r': + // This differs from CSS3's wc production because it contains a + // probable spec error whereby wc contains all the single byte + // sequences in nl (newline) but not CRLF. + if len(c) >= 2 && c[1] == '\n' { + return c[2:] + } + return c[1:] + } + return c +} + +// cssEscaper escapes HTML and CSS special characters using \<hex>+ escapes. +func cssEscaper(args ...interface{}) string { + s, _ := stringify(args...) + var b bytes.Buffer + written := 0 + for i, r := range s { + var repl string + switch r { + case 0: + repl = `\0` + case '\t': + repl = `\9` + case '\n': + repl = `\a` + case '\f': + repl = `\c` + case '\r': + repl = `\d` + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + case '"': + repl = `\22` + case '&': + repl = `\26` + case '\'': + repl = `\27` + case '(': + repl = `\28` + case ')': + repl = `\29` + case '+': + repl = `\2b` + case '/': + repl = `\2f` + case ':': + repl = `\3a` + case ';': + repl = `\3b` + case '<': + repl = `\3c` + case '>': + repl = `\3e` + case '\\': + repl = `\\` + case '{': + repl = `\7b` + case '}': + repl = `\7d` + default: + continue + } + b.WriteString(s[written:i]) + b.WriteString(repl) + written = i + utf8.RuneLen(r) + if repl != `\\` && (written == len(s) || isHex(s[written])) { + b.WriteByte(' ') + } + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} + +var expressionBytes = []byte("expression") +var mozBindingBytes = []byte("mozbinding") + +// cssValueFilter allows innocuous CSS values in the output including CSS +// quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values +// (inherit, blue), and colors (#888). +// It filters out unsafe values, such as those that affect token boundaries, +// and anything that might execute scripts. +func cssValueFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeCSS { + return s + } + b, id := decodeCSS([]byte(s)), make([]byte, 0, 64) + + // CSS3 error handling is specified as honoring string boundaries per + // http://www.w3.org/TR/css3-syntax/#error-handling : + // Malformed declarations. User agents must handle unexpected + // tokens encountered while parsing a declaration by reading until + // the end of the declaration, while observing the rules for + // matching pairs of (), [], {}, "", and '', and correctly handling + // escapes. For example, a malformed declaration may be missing a + // property, colon (:) or value. + // So we need to make sure that values do not have mismatched bracket + // or quote characters to prevent the browser from restarting parsing + // inside a string that might embed JavaScript source. + for i, c := range b { + switch c { + case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}': + return filterFailsafe + case '-': + // Disallow <!-- or -->. + // -- should not appear in valid identifiers. + if i != 0 && '-' == b[i-1] { + return filterFailsafe + } + default: + if c < 0x80 && isCSSNmchar(int(c)) { + id = append(id, c) + } + } + } + id = bytes.ToLower(id) + if bytes.Index(id, expressionBytes) != -1 || bytes.Index(id, mozBindingBytes) != -1 { + return filterFailsafe + } + return string(b) +} diff --git a/src/pkg/exp/template/html/css_test.go b/src/pkg/exp/template/html/css_test.go new file mode 100644 index 000000000..5ba3e77bb --- /dev/null +++ b/src/pkg/exp/template/html/css_test.go @@ -0,0 +1,277 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "strconv" + "strings" + "testing" +) + +func TestEndsWithCSSKeyword(t *testing.T) { + tests := []struct { + css, kw string + want bool + }{ + {"", "url", false}, + {"url", "url", true}, + {"URL", "url", true}, + {"Url", "url", true}, + {"url", "important", false}, + {"important", "important", true}, + {"image-url", "url", false}, + {"imageurl", "url", false}, + {"image url", "url", true}, + } + for _, test := range tests { + got := endsWithCSSKeyword([]byte(test.css), test.kw) + if got != test.want { + t.Errorf("want %t but got %t for css=%v, kw=%v", test.want, got, test.css, test.kw) + } + } +} + +func TestIsCSSNmchar(t *testing.T) { + tests := []struct { + rune int + want bool + }{ + {0, false}, + {'0', true}, + {'9', true}, + {'A', true}, + {'Z', true}, + {'a', true}, + {'z', true}, + {'_', true}, + {'-', true}, + {':', false}, + {';', false}, + {' ', false}, + {0x7f, false}, + {0x80, true}, + {0x1234, true}, + {0xd800, false}, + {0xdc00, false}, + {0xfffe, false}, + {0x10000, true}, + {0x110000, false}, + } + for _, test := range tests { + got := isCSSNmchar(test.rune) + if got != test.want { + t.Errorf("%q: want %t but got %t", string(test.rune), test.want, got) + } + } +} + +func TestDecodeCSS(t *testing.T) { + tests := []struct { + css, want string + }{ + {``, ``}, + {`foo`, `foo`}, + {`foo\`, `foo`}, + {`foo\\`, `foo\`}, + {`\`, ``}, + {`\A`, "\n"}, + {`\a`, "\n"}, + {`\0a`, "\n"}, + {`\00000a`, "\n"}, + {`\000000a`, "\u0000a"}, + {`\1234 5`, "\u1234" + "5"}, + {`\1234\20 5`, "\u1234" + " 5"}, + {`\1234\A 5`, "\u1234" + "\n5"}, + {"\\1234\t5", "\u1234" + "5"}, + {"\\1234\n5", "\u1234" + "5"}, + {"\\1234\r\n5", "\u1234" + "5"}, + {`\12345`, "\U00012345"}, + {`\\`, `\`}, + {`\\ `, `\ `}, + {`\"`, `"`}, + {`\'`, `'`}, + {`\.`, `.`}, + {`\. .`, `. .`}, + { + `The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3e dog\3c/canine\3e`, + "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>", + }, + } + for _, test := range tests { + got := string(decodeCSS([]byte(test.css))) + if got != test.want { + t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.css, test.want, got) + } + } +} + +func TestHexDecode(t *testing.T) { + for i := 0; i < 0x200000; i += 101 /* coprime with 16 */ { + s := strconv.Itob(i, 16) + if got := hexDecode([]byte(s)); got != i { + t.Errorf("%s: want %d but got %d", s, i, got) + } + s = strings.ToUpper(s) + if got := hexDecode([]byte(s)); got != i { + t.Errorf("%s: want %d but got %d", s, i, got) + } + } +} + +func TestSkipCSSSpace(t *testing.T) { + tests := []struct { + css, want string + }{ + {"", ""}, + {"foo", "foo"}, + {"\n", ""}, + {"\r\n", ""}, + {"\r", ""}, + {"\t", ""}, + {" ", ""}, + {"\f", ""}, + {" foo", "foo"}, + {" foo", " foo"}, + {`\20`, `\20`}, + } + for _, test := range tests { + got := string(skipCSSSpace([]byte(test.css))) + if got != test.want { + t.Errorf("%q: want %q but got %q", test.css, test.want, got) + } + } +} + +func TestCSSEscaper(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + want := ("\\0\x01\x02\x03\x04\x05\x06\x07" + + "\x08\\9\\a\x0b\\c\\d\x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !\22#$%\26\27\28\29*\2b,-.\2f ` + + `0123456789\3a\3b\3c=\3e?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\\]^_` + + "`abcdefghijklmno" + + `pqrstuvwxyz\7b|\7d~` + "\u007f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + got := cssEscaper(input) + if got != want { + t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got) + } + + got = string(decodeCSS([]byte(got))) + if input != got { + t.Errorf("decode: want\n\t%q\nbut got\n\t%q", input, got) + } +} + +func TestCSSValueFilter(t *testing.T) { + tests := []struct { + css, want string + }{ + {"", ""}, + {"foo", "foo"}, + {"0", "0"}, + {"0px", "0px"}, + {"-5px", "-5px"}, + {"1.25in", "1.25in"}, + {"+.33em", "+.33em"}, + {"100%", "100%"}, + {"12.5%", "12.5%"}, + {".foo", ".foo"}, + {"#bar", "#bar"}, + {"corner-radius", "corner-radius"}, + {"-moz-corner-radius", "-moz-corner-radius"}, + {"#000", "#000"}, + {"#48f", "#48f"}, + {"#123456", "#123456"}, + {"U+00-FF, U+980-9FF", "U+00-FF, U+980-9FF"}, + {"color: red", "color: red"}, + {"<!--", "ZgotmplZ"}, + {"-->", "ZgotmplZ"}, + {"<![CDATA[", "ZgotmplZ"}, + {"]]>", "ZgotmplZ"}, + {"</style", "ZgotmplZ"}, + {`"`, "ZgotmplZ"}, + {`'`, "ZgotmplZ"}, + {"`", "ZgotmplZ"}, + {"\x00", "ZgotmplZ"}, + {"/* foo */", "ZgotmplZ"}, + {"//", "ZgotmplZ"}, + {"[href=~", "ZgotmplZ"}, + {"expression(alert(1337))", "ZgotmplZ"}, + {"-expression(alert(1337))", "ZgotmplZ"}, + {"expression", "ZgotmplZ"}, + {"Expression", "ZgotmplZ"}, + {"EXPRESSION", "ZgotmplZ"}, + {"-moz-binding", "ZgotmplZ"}, + {"-expr\x00ession(alert(1337))", "ZgotmplZ"}, + {`-expr\0ession(alert(1337))`, "ZgotmplZ"}, + {`-express\69on(alert(1337))`, "ZgotmplZ"}, + {`-express\69 on(alert(1337))`, "ZgotmplZ"}, + {`-exp\72 ession(alert(1337))`, "ZgotmplZ"}, + {`-exp\52 ession(alert(1337))`, "ZgotmplZ"}, + {`-exp\000052 ession(alert(1337))`, "ZgotmplZ"}, + {`-expre\0000073sion`, "-expre\x073sion"}, + {`@import url evil.css`, "ZgotmplZ"}, + } + for _, test := range tests { + got := cssValueFilter(test.css) + if got != test.want { + t.Errorf("%q: want %q but got %q", test.css, test.want, got) + } + } +} + +func BenchmarkCSSEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + cssEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkCSSEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + cssEscaper("The quick, brown fox jumps over the lazy dog.") + } +} + +func BenchmarkDecodeCSS(b *testing.B) { + s := []byte(`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3edog\3c/canine\3e`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeCSS(s) + } +} + +func BenchmarkDecodeCSSNoSpecials(b *testing.B) { + s := []byte("The quick, brown fox jumps over the lazy dog.") + b.ResetTimer() + for i := 0; i < b.N; i++ { + decodeCSS(s) + } +} + +func BenchmarkCSSValueFilter(b *testing.B) { + for i := 0; i < b.N; i++ { + cssValueFilter(` e\78preS\0Sio/**/n(alert(1337))`) + } +} + +func BenchmarkCSSValueFilterOk(b *testing.B) { + for i := 0; i < b.N; i++ { + cssValueFilter(`Times New Roman`) + } +} diff --git a/src/pkg/exp/template/html/doc.go b/src/pkg/exp/template/html/doc.go new file mode 100644 index 000000000..a9b78ca51 --- /dev/null +++ b/src/pkg/exp/template/html/doc.go @@ -0,0 +1,190 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package html is a specialization of package template that automates the +construction of HTML output that is safe against code injection. + + +Introduction + +To use this package, invoke the standard template package to parse a template +set, and then use this package’s EscapeSet function to secure the set. +The arguments to EscapeSet are the template set and the names of all templates +that will be passed to Execute. + + set, err := new(template.Set).Parse(...) + set, err = EscapeSet(set, "templateName0", ...) + +If successful, set will now be injection-safe. Otherwise, the returned set will +be nil and an error, described below, will explain the problem. + +The template names do not need to include helper templates but should include +all names x used thus: + + set.Execute(out, x, ...) + +EscapeSet modifies the named templates in place to treat data values as plain +text safe for embedding in an HTML document. The escaping is contextual, so +actions can appear within JavaScript, CSS, and URI contexts without introducing'hazards. + +The security model used by this package assumes that template authors are +trusted, while Execute's data parameter is not. More details are provided below. + +Example + + tmpls, err := new(template.Set).Parse(`{{define "t'}}Hello, {{.}}!{{end}}`) + +when used by itself + + tmpls.Execute(out, "t", "<script>alert('you have been pwned')</script>") + +produces + + Hello, <script>alert('you have been pwned')</script>! + +but after securing with EscapeSet like this, + + tmpls, err := EscapeSet(tmpls, "t") + tmpls.Execute(out, "t", ...) + +produces the safe, escaped HTML output + + Hello, <script>alert('you have been pwned')</script>! + + +Contexts + +EscapeSet understands HTML, CSS, JavaScript, and URIs. It adds sanitizing +functions to each simple action pipeline, so given the excerpt + + <a href="/search?q={{.}}">{{.}}</a> + +EscapeSet will rewrite each {{.}} to add escaping functions where necessary, +in this case, + + <a href="/search?q={{. | urlquery}}">{{. | html}}</a> + + +Errors + +See the documentation of ErrorCode for details. + + +A fuller picture + +The rest of this package comment may be skipped on first reading; it includes +details necessary to understand escaping contexts and error messages. Most users +will not need to understand these details. + + +Contexts + +Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows +how {{.}} appears when used in the context to the left. + +Context {{.}} After +{{.}} O'Reilly: How are <i>you</i>? +<a title='{{.}}'> O'Reilly: How are you? +<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e? +<a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f +<a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...? +<a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?" +<a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f + +If used in an unsafe context, then the value might be filtered out: + +Context {{.}} After +<a href="{{.}}"> #ZgotmplZ + +since "O'Reilly:" is not an allowed protocol like "http:". + + +If {{.}} is the innocuous word, `left`, then it can appear more widely, + +Context {{.}} After +{{.}} left +<a title='{{.}}'> left +<a href='{{.}}'> left +<a href='/{{.}}'> left +<a href='?dir={{.}}'> left +<a style="border-{{.}}: 4px"> left +<a style="align: {{.}}"> left +<a style="background: '{{.}}'> left +<a style="background: url('{{.}}')> left +<style>p.{{.}} {color:red}</style> left + +Non-string values can be used in JavaScript contexts. +If {{.}} is + + []struct{A,B string}{ "foo", "bar" } + +in the escaped template + + <script>var pair = {{.}};</script> + +then the template output is + + <script>var pair = {"A": "foo", "B": "bar"};</script> + +See package json to understand how non-string content is marshalled for +embedding in JavaScript contexts. + + +Typed Strings + +By default, EscapeSet assumes all pipelines produce a plain text string. It +adds escaping pipeline stages necessary to correctly and safely embed that +plain text string in the appropriate context. + +When a data value is not plain text, you can make sure it is not over-escaped +by marking it with its type. + +Types HTML, JS, URL, and others from content.go can carry safe content that is +exempted from escaping. + +The template + + Hello, {{.}}! + +can be invoked with + + tmpl.Execute(out, HTML(`<b>World</b>`)) + +to produce + + Hello, <b>World</b>! + +instead of the + + Hello, <b>World<b>! + +that would have been produced if {{.}} was a regular string. + + +Security Model + +http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. + +This package assumes that template authors are trusted, that Execute's data +parameter is not, and seeks to preserve the properties below in the face +of untrusted data: + +Structure Preservation Property +"... when a template author writes an HTML tag in a safe templating language, +the browser will interpret the corresponding portion of the output as a tag +regardless of the values of untrusted data, and similarly for other structures +such as attribute boundaries and JS and CSS string boundaries." + +Code Effect Property +"... only code specified by the template author should run as a result of +injecting the template output into a page and all code specified by the +template author should run as a result of the same." + +Least Surprise Property +"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript; +who knows that EscapeSet is applied should be able to look at a {{.}} +and correctly infer what sanitization happens." +*/ +package html diff --git a/src/pkg/exp/template/html/error.go b/src/pkg/exp/template/html/error.go new file mode 100644 index 000000000..f06251d60 --- /dev/null +++ b/src/pkg/exp/template/html/error.go @@ -0,0 +1,181 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "fmt" +) + +// Error describes a problem encountered during template Escaping. +type Error struct { + // ErrorCode describes the kind of error. + ErrorCode ErrorCode + // Name is the name of the template in which the error was encountered. + Name string + // Line is the line number of the error in the template source or 0. + Line int + // Description is a human-readable description of the problem. + Description string +} + +// ErrorCode is a code for a kind of error. +type ErrorCode int + +// We define codes for each error that manifests while escaping templates, but +// escaped templates may also fail at runtime. +// +// Output: "ZgotmplZ" +// Example: +// <img src="{{.X}}"> +// where {{.X}} evaluates to `javascript:...` +// Discussion: +// "ZgotmplZ" is a special value that indicates that unsafe content reached a +// CSS or URL context at runtime. The output of the example will be +// <img src="#ZgotmplZ"> +// If the data comes from a trusted source, use content types to exempt it +// from filtering: URL(`javascript:...`). +const ( + // OK indicates the lack of an error. + OK ErrorCode = iota + + // ErrorAmbigContext: "... appears in an ambiguous URL context" + // Example: + // <a href=" + // {{if .C}} + // /path/ + // {{else}} + // /search?q= + // {{end}} + // {{.X}} + // "> + // Discussion: + // {{.X}} is in an ambiguous URL context since, depending on {{.C}}, + // it may be either a URL suffix or a query parameter. + // Moving {{.X}} into the condition removes the ambiguity: + // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}"> + ErrAmbigContext + + // TODO: document + ErrBadHTML + + // ErrBranchEnd: "{{if}} branches end in different contexts" + // Example: + // {{if .C}}<a href="{{end}}{{.X}} + // Discussion: + // EscapeSet statically examines each possible path when it encounters + // a {{if}}, {{range}}, or {{with}} to escape any following pipelines. + // The example is ambiguous since {{.X}} might be an HTML text node, + // or a URL prefix in an HTML attribute. EscapeSet needs to understand + // the context of {{.X}} to escape it, but that depends on the + // run-time value of {{.C}}. + // + // The problem is usually something like missing quotes or angle + // brackets, or can be avoided by refactoring to put the two contexts + // into different branches of an if, range or with. If the problem + // is in a {{range}} over a collection that should never be empty, + // adding a dummy {{else}} can help. + ErrBranchEnd + + // ErrEndContext: "... ends in a non-text context: ..." + // Examples: + // <div + // <div title="no close quote> + // <script>f() + // Discussion: + // EscapeSet assumes the ouput is a DocumentFragment of HTML. + // Templates that end without closing tags will trigger this error. + // Templates that produce incomplete Fragments should not be named + // in the call to EscapeSet. + // + // If you have a helper template in your set that is not meant to + // produce a document fragment, then do not pass its name to + // EscapeSet(set, ...names). + // + // {{define "main"}} <script>{{template "helper"}}</script> {{end}} + // {{define "helper"}} document.write(' <div title=" ') {{end}} + // + // "helper" does not produce a valid document fragment, though it does + // produce a valid JavaScript Program. + ErrEndContext + + // ErrNoNames: "must specify names of top level templates" + // + // EscapeSet does not assume that all templates in a set produce HTML. + // Some may be helpers that produce snippets of other languages. + // Passing in no template names is most likely an error, + // so EscapeSet(set) will panic. + // If you call EscapeSet with a slice of names, guard it with len: + // + // if len(names) != 0 { + // set, err := EscapeSet(set, ...names) + // } + ErrNoNames + + // ErrNoSuchTemplate: "no such template ..." + // Examples: + // {{define "main"}}<div {{template "attrs"}}>{{end}} + // {{define "attrs"}}href="{{.URL}}"{{end}} + // Discussion: + // EscapeSet looks through template calls to compute the context. + // Here the {{.URL}} in "attrs" must be treated as a URL when called + // from "main", but if "attrs" is not in set when + // EscapeSet(&set, "main") is called, this error will arise. + ErrNoSuchTemplate + + // TODO: document + ErrOutputContext + + // ErrPartialCharset: "unfinished JS regexp charset in ..." + // Example: + // <script>var pattern = /foo[{{.Chars}}]/</script> + // Discussion: + // EscapeSet does not support interpolation into regular expression + // literal character sets. + ErrPartialCharset + + // ErrPartialEscape: "unfinished escape sequence in ..." + // Example: + // <script>alert("\{{.X}}")</script> + // Discussion: + // EscapeSet does not support actions following a backslash. + // This is usually an error and there are better solutions; for + // our example + // <script>alert("{{.X}}")</script> + // should work, and if {{.X}} is a partial escape sequence such as + // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`) + ErrPartialEscape + + // ErrRangeLoopReentry: "on range loop re-entry: ..." + // Example: + // {{range .}}<p class={{.}}{{end}} + // Discussion: + // If an iteration through a range would cause it to end in a + // different context than an earlier pass, there is no single context. + // In the example, the <p> tag is missing a '>'. + // EscapeSet cannot tell whether {{.}} is meant to be an HTML class or + // the content of a broken <p> element and complains because the + // second iteration would produce something like + // + // <p class=foo<p class=bar + ErrRangeLoopReentry + + // TODO: document + ErrSlashAmbig +) + +func (e *Error) String() string { + if e.Line != 0 { + return fmt.Sprintf("exp/template/html:%s:%d: %s", e.Name, e.Line, e.Description) + } else if e.Name != "" { + return fmt.Sprintf("exp/template/html:%s: %s", e.Name, e.Description) + } + return "exp/template/html: " + e.Description +} + +// errorf creates an error given a format string f and args. +// The template Name still needs to be supplied. +func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error { + return &Error{k, "", line, fmt.Sprintf(f, args...)} +} diff --git a/src/pkg/exp/template/html/escape.go b/src/pkg/exp/template/html/escape.go index 0eb8dfec8..650a6acd2 100644 --- a/src/pkg/exp/template/html/escape.go +++ b/src/pkg/exp/template/html/escape.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package html is a specialization of template that automates the -// construction of safe HTML output. -// INCOMPLETE. package html import ( @@ -12,93 +9,212 @@ import ( "fmt" "html" "os" - "strings" "template" "template/parse" ) // Escape rewrites each action in the template to guarantee that the output is -// HTML-escaped. +// properly escaped. func Escape(t *template.Template) (*template.Template, os.Error) { - c := escapeList(context{}, t.Tree.Root) - if c.errStr != "" { - return nil, fmt.Errorf("%s:%d: %s", t.Name(), c.errLine, c.errStr) + var s template.Set + s.Add(t) + if _, err := EscapeSet(&s, t.Name()); err != nil { + return nil, err } - if c.state != stateText { - return nil, fmt.Errorf("%s ends in a non-text context: %v", t.Name(), c) - } - t.Funcs(funcMap) + // TODO: if s contains cloned dependencies due to self-recursion + // cross-context, error out. return t, nil } +// EscapeSet rewrites the template set to guarantee that the output of any of +// the named templates is properly escaped. +// Names should include the names of all templates that might be Executed but +// need not include helper templates. +// If no error is returned, then the named templates have been modified. +// Otherwise the named templates have been rendered unusable. +func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) { + if len(names) == 0 { + // TODO: Maybe add a method to Set to enumerate template names + // and use those instead. + return nil, &Error{ErrNoNames, "", 0, "must specify names of top level templates"} + } + e := newEscaper(s) + for _, name := range names { + c, _ := e.escapeTree(context{}, name, 0) + var err os.Error + if c.err != nil { + err, c.err.Name = c.err, name + } else if c.state != stateText { + err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)} + } + if err != nil { + // Prevent execution of unsafe templates. + for _, name := range names { + if t := s.Template(name); t != nil { + t.Tree = nil + } + } + return nil, err + } + } + e.commit() + return s, nil +} + // funcMap maps command names to functions that render their inputs safe. var funcMap = template.FuncMap{ - "exp_template_html_urlfilter": urlFilter, - "exp_template_html_jsvalescaper": jsValEscaper, - "exp_template_html_jsstrescaper": jsStrEscaper, + "exp_template_html_attrescaper": attrEscaper, + "exp_template_html_commentescaper": commentEscaper, + "exp_template_html_cssescaper": cssEscaper, + "exp_template_html_cssvaluefilter": cssValueFilter, + "exp_template_html_htmlnamefilter": htmlNameFilter, + "exp_template_html_htmlescaper": htmlEscaper, "exp_template_html_jsregexpescaper": jsRegexpEscaper, + "exp_template_html_jsstrescaper": jsStrEscaper, + "exp_template_html_jsvalescaper": jsValEscaper, + "exp_template_html_nospaceescaper": htmlNospaceEscaper, + "exp_template_html_rcdataescaper": rcdataEscaper, + "exp_template_html_urlescaper": urlEscaper, + "exp_template_html_urlfilter": urlFilter, + "exp_template_html_urlnormalizer": urlNormalizer, +} + +// equivEscapers matches contextual escapers to equivalent template builtins. +var equivEscapers = map[string]string{ + "exp_template_html_attrescaper": "html", + "exp_template_html_htmlescaper": "html", + "exp_template_html_nospaceescaper": "html", + "exp_template_html_rcdataescaper": "html", + "exp_template_html_urlescaper": "urlquery", + "exp_template_html_urlnormalizer": "urlquery", +} + +// escaper collects type inferences about templates and changes needed to make +// templates injection safe. +type escaper struct { + // set is the template set being escaped. + set *template.Set + // output[templateName] is the output context for a templateName that + // has been mangled to include its input context. + output map[string]context + // derived[c.mangle(name)] maps to a template derived from the template + // named name templateName for the start context c. + derived map[string]*template.Template + // called[templateName] is a set of called mangled template names. + called map[string]bool + // xxxNodeEdits are the accumulated edits to apply during commit. + // Such edits are not applied immediately in case a template set + // executes a given template in different escaping contexts. + actionNodeEdits map[*parse.ActionNode][]string + templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte +} + +// newEscaper creates a blank escaper for the given set. +func newEscaper(s *template.Set) *escaper { + return &escaper{ + s, + map[string]context{}, + map[string]*template.Template{}, + map[string]bool{}, + map[*parse.ActionNode][]string{}, + map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, + } } +// filterFailsafe is an innocuous word that is emitted in place of unsafe values +// by sanitizer functions. It is not a keyword in any programming language, +// contains no special characters, is not empty, and when it appears in output +// it is distinct enough that a developer can find the source of the problem +// via a search engine. +const filterFailsafe = "ZgotmplZ" + // escape escapes a template node. -func escape(c context, n parse.Node) context { +func (e *escaper) escape(c context, n parse.Node) context { switch n := n.(type) { case *parse.ActionNode: - return escapeAction(c, n) + return e.escapeAction(c, n) case *parse.IfNode: - return escapeBranch(c, &n.BranchNode, "if") + return e.escapeBranch(c, &n.BranchNode, "if") case *parse.ListNode: - return escapeList(c, n) + return e.escapeList(c, n) case *parse.RangeNode: - return escapeBranch(c, &n.BranchNode, "range") + return e.escapeBranch(c, &n.BranchNode, "range") + case *parse.TemplateNode: + return e.escapeTemplate(c, n) case *parse.TextNode: - return escapeText(c, n.Text) + return e.escapeText(c, n) case *parse.WithNode: - return escapeBranch(c, &n.BranchNode, "with") + return e.escapeBranch(c, &n.BranchNode, "with") } - // TODO: handle a *parse.TemplateNode. Should Escape take a *template.Set? panic("escaping " + n.String() + " is unimplemented") } // escapeAction escapes an action template node. -func escapeAction(c context, n *parse.ActionNode) context { - s := make([]string, 0, 2) +func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { + c = nudge(c) + s := make([]string, 0, 3) switch c.state { - case stateURL: + case stateError: + return c + case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL: switch c.urlPart { case urlPartNone: s = append(s, "exp_template_html_urlfilter") - case urlPartQueryOrFrag: - s = append(s, "urlquery") + fallthrough case urlPartPreQuery: - s = append(s, "html") + switch c.state { + case stateCSSDqStr, stateCSSSqStr: + s = append(s, "exp_template_html_cssescaper") + case stateCSSDqURL, stateCSSSqURL, stateCSSURL: + s = append(s, "exp_template_html_urlnormalizer") + } + case urlPartQueryOrFrag: + s = append(s, "exp_template_html_urlescaper") case urlPartUnknown: return context{ - state: stateError, - errLine: n.Line, - errStr: fmt.Sprintf("%s appears in an ambiguous URL context", n), + state: stateError, + err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n), } default: panic(c.urlPart.String()) } case stateJS: s = append(s, "exp_template_html_jsvalescaper") - if c.delim != delimNone { - s = append(s, "html") - } + // A slash after a value starts a div operator. + c.jsCtx = jsCtxDivOp case stateJSDqStr, stateJSSqStr: s = append(s, "exp_template_html_jsstrescaper") case stateJSRegexp: s = append(s, "exp_template_html_jsregexpescaper") - case stateJSBlockCmt, stateJSLineCmt: - return context{ - state: stateError, - errLine: n.Line, - errStr: fmt.Sprintf("%s appears inside a comment", n), + case stateCSS: + s = append(s, "exp_template_html_cssvaluefilter") + case stateText: + s = append(s, "exp_template_html_htmlescaper") + case stateRCDATA: + s = append(s, "exp_template_html_rcdataescaper") + case stateAttr: + // Handled below in delim check. + case stateAttrName, stateTag: + c.state = stateAttrName + s = append(s, "exp_template_html_htmlnamefilter") + default: + if isComment(c.state) { + s = append(s, "exp_template_html_commentescaper") + } else { + panic("unexpected state " + c.state.String()) } + } + switch c.delim { + case delimNone: + // No extra-escaping needed for raw text content. + case delimSpaceOrTagEnd: + s = append(s, "exp_template_html_nospaceescaper") default: - s = append(s, "html") + s = append(s, "exp_template_html_attrescaper") } - ensurePipelineContains(n.Pipe, s) + e.editActionNode(n, s) return c } @@ -117,7 +233,10 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { idents := p.Cmds for i := n - 1; i >= 0; i-- { if cmd := p.Cmds[i]; len(cmd.Args) != 0 { - if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok { + if id, ok := cmd.Args[0].(*parse.IdentifierNode); ok { + if id.Ident == "noescape" { + return + } continue } } @@ -125,7 +244,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { } dups := 0 for _, id := range idents { - if s[dups] == (id.Args[0].(*parse.IdentifierNode)).Ident { + if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) { dups++ if dups == len(s) { return @@ -136,7 +255,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { copy(newCmds, p.Cmds) // Merge existing identifier commands with the sanitizers needed. for _, id := range idents { - i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s) + i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) if i != -1 { for _, name := range s[:i] { newCmds = append(newCmds, newIdentCmd(name)) @@ -152,16 +271,27 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { p.Cmds = newCmds } -// indexOfStr is the least i such that strs[i] == s or -1 if s is not in strs. -func indexOfStr(s string, strs []string) int { +// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found. +func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { for i, t := range strs { - if s == t { + if eq(s, t) { return i } } return -1 } +// escFnsEq returns whether the two escaping functions are equivalent. +func escFnsEq(a, b string) bool { + if e := equivEscapers[a]; e != "" { + a = e + } + if e := equivEscapers[b]; e != "" { + b = e + } + return a == b +} + // newIdentCmd produces a command containing a single identifier node. func newIdentCmd(identifier string) *parse.CommandNode { return &parse.CommandNode{ @@ -170,6 +300,33 @@ func newIdentCmd(identifier string) *parse.CommandNode { } } +// nudge returns the context that would result from following empty string +// transitions from the input context. +// For example, parsing: +// `<a href=` +// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune: +// `<a href=x` +// will end in context{stateURL, delimSpaceOrTagEnd, ...}. +// There are two transitions that happen when the 'x' is seen: +// (1) Transition from a before-value state to a start-of-value state without +// consuming any character. +// (2) Consume 'x' and transition past the first value character. +// In this case, nudging produces the context after (1) happens. +func nudge(c context) context { + switch c.state { + case stateTag: + // In `<foo {{.}}`, the action should emit an attribute. + c.state = stateAttrName + case stateBeforeValue: + // In `<foo bar={{.}}`, the action is an undelimited value. + c.state, c.delim, c.attr = attrStartStates[c.attr], delimSpaceOrTagEnd, attrNone + case stateAfterName: + // In `<foo bar {{.}}`, the action is an attribute name. + c.state, c.attr = stateAttrName, attrNone + } + return c +} + // join joins the two contexts of a branch template node. The result is an // error context if either of the input contexts are error contexts, or if the // the input contexts differ. @@ -192,448 +349,351 @@ func join(a, b context, line int, nodeName string) context { return c } + c = a + c.jsCtx = b.jsCtx + if c.eq(b) { + // The contexts differ only by jsCtx. + c.jsCtx = jsCtxUnknown + return c + } + + // Allow a nudged context to join with an unnudged one. + // This means that + // <p title={{if .C}}{{.}}{{end}} + // ends in an unquoted value state even though the else branch + // ends in stateBeforeValue. + if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) { + if e := join(c, d, line, nodeName); e.state != stateError { + return e + } + } + return context{ - state: stateError, - errLine: line, - errStr: fmt.Sprintf("{{%s}} branches end in different contexts: %v, %v", nodeName, a, b), + state: stateError, + err: errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b), } } // escapeBranch escapes a branch template node: "if", "range" and "with". -func escapeBranch(c context, n *parse.BranchNode, nodeName string) context { - c0 := escapeList(c, n.List) +func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context { + c0 := e.escapeList(c, n.List) if nodeName == "range" && c0.state != stateError { // The "true" branch of a "range" node can execute multiple times. // We check that executing n.List once results in the same context // as executing n.List twice. - c0 = join(c0, escapeList(c0, n.List), n.Line, nodeName) + c1, _ := e.escapeListConditionally(c0, n.List, nil) + c0 = join(c0, c1, n.Line, nodeName) if c0.state == stateError { // Make clear that this is a problem on loop re-entry // since developers tend to overlook that branch when // debugging templates. - c0.errLine = n.Line - c0.errStr = "on range loop re-entry: " + c0.errStr + c0.err.Line = n.Line + c0.err.Description = "on range loop re-entry: " + c0.err.Description return c0 } } - c1 := escapeList(c, n.ElseList) + c1 := e.escapeList(c, n.ElseList) return join(c0, c1, n.Line, nodeName) } // escapeList escapes a list template node. -func escapeList(c context, n *parse.ListNode) context { +func (e *escaper) escapeList(c context, n *parse.ListNode) context { if n == nil { return c } for _, m := range n.Nodes { - c = escape(c, m) + c = e.escape(c, m) } return c } -// delimEnds maps each delim to a string of characters that terminate it. -var delimEnds = [...]string{ - delimDoubleQuote: `"`, - delimSingleQuote: "'", - // Determined empirically by running the below in various browsers. - // var div = document.createElement("DIV"); - // for (var i = 0; i < 0x10000; ++i) { - // div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>"; - // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0) - // document.write("<p>U+" + i.toString(16)); - // } - delimSpaceOrTagEnd: " \t\n\f\r>", -} - -// escapeText escapes a text template node. -func escapeText(c context, s []byte) context { - for len(s) > 0 { - if c.delim == delimNone { - c, s = transitionFunc[c.state](c, s) - continue +// escapeListConditionally escapes a list node but only preserves edits and +// inferences in e if the inferences and output context satisfy filter. +// It returns the best guess at an output context, and the result of the filter +// which is the same as whether e was updated. +func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) { + e1 := newEscaper(e.set) + // Make type inferences available to f. + for k, v := range e.output { + e1.output[k] = v + } + c = e1.escapeList(c, n) + ok := filter != nil && filter(e1, c) + if ok { + // Copy inferences and edits from e1 back into e. + for k, v := range e1.output { + e.output[k] = v } - - i := bytes.IndexAny(s, delimEnds[c.delim]) - if i == -1 { - // Remain inside the attribute. - // Decode the value so non-HTML rules can easily handle - // <button onclick="alert("Hi!")"> - // without having to entity decode token boundaries. - d := c.delim - c.delim = delimNone - c = escapeText(c, []byte(html.UnescapeString(string(s)))) - if c.state != stateError { - c.delim = d - } - return c + for k, v := range e1.derived { + e.derived[k] = v } - if c.delim != delimSpaceOrTagEnd { - // Consume any quote. - i++ + for k, v := range e1.called { + e.called[k] = v } - c, s = context{state: stateTag}, s[i:] - } - return c -} - -// transitionFunc is the array of context transition functions for text nodes. -// A transition function takes a context and template text input, and returns -// the updated context and any unconsumed text. -var transitionFunc = [...]func(context, []byte) (context, []byte){ - stateText: tText, - stateTag: tTag, - stateURL: tURL, - stateJS: tJS, - stateJSDqStr: tJSStr, - stateJSSqStr: tJSStr, - stateJSRegexp: tJSRegexp, - stateJSBlockCmt: tJSBlockCmt, - stateJSLineCmt: tJSLineCmt, - stateAttr: tAttr, - stateError: tError, -} - -// tText is the context transition function for the text state. -func tText(c context, s []byte) (context, []byte) { - for { - i := bytes.IndexByte(s, '<') - if i == -1 || i+1 == len(s) { - return c, nil + for k, v := range e1.actionNodeEdits { + e.editActionNode(k, v) } - i++ - if s[i] == '/' { - if i+1 == len(s) { - return c, nil - } - i++ + for k, v := range e1.templateNodeEdits { + e.editTemplateNode(k, v) } - j := eatTagName(s, i) - if j != i { - // We've found an HTML tag. - return context{state: stateTag}, s[j:] + for k, v := range e1.textNodeEdits { + e.editTextNode(k, v) } - s = s[j:] } - panic("unreachable") + return c, ok } -// tTag is the context transition function for the tag state. -func tTag(c context, s []byte) (context, []byte) { - // Find the attribute name. - attrStart := eatWhiteSpace(s, 0) - i, err := eatAttrName(s, attrStart) - if err != nil { - return context{ - state: stateError, - errStr: err.String(), - }, nil - } - if i == len(s) { - return context{state: stateTag}, nil - } - state := stateAttr - canonAttrName := strings.ToLower(string(s[attrStart:i])) - if urlAttr[canonAttrName] { - state = stateURL - } else if strings.HasPrefix(canonAttrName, "on") { - state = stateJS - } - - // Look for the start of the value. - i = eatWhiteSpace(s, i) - if i == len(s) { - return context{state: stateTag}, s[i:] - } - if s[i] == '>' { - return context{state: stateText}, s[i+1:] - } else if s[i] != '=' { - // Possible due to a valueless attribute or '/' in "<input />". - return context{state: stateTag}, s[i:] - } - // Consume the "=". - i = eatWhiteSpace(s, i+1) - - // Find the attribute delimiter. - delim := delimSpaceOrTagEnd - if i < len(s) { - switch s[i] { - case '\'': - delim, i = delimSingleQuote, i+1 - case '"': - delim, i = delimDoubleQuote, i+1 - } +// escapeTemplate escapes a {{template}} call node. +func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context { + c, name := e.escapeTree(c, n.Name, n.Line) + if name != n.Name { + e.editTemplateNode(n, name) } - - return context{state: state, delim: delim}, s[i:] -} - -// tAttr is the context transition function for the attribute state. -func tAttr(c context, s []byte) (context, []byte) { - return c, nil + return c } -// tURL is the context transition function for the URL state. -func tURL(c context, s []byte) (context, []byte) { - if bytes.IndexAny(s, "#?") >= 0 { - c.urlPart = urlPartQueryOrFrag - } else if c.urlPart == urlPartNone { - c.urlPart = urlPartPreQuery +// escapeTree escapes the named template starting in the given context as +// necessary and returns its output context. +func (e *escaper) escapeTree(c context, name string, line int) (context, string) { + // Mangle the template name with the input context to produce a reliable + // identifier. + dname := c.mangle(name) + e.called[dname] = true + if out, ok := e.output[dname]; ok { + // Already escaped. + return out, dname + } + t := e.template(name) + if t == nil { + return context{ + state: stateError, + err: errorf(ErrNoSuchTemplate, line, "no such template %s", name), + }, dname + } + if dname != name { + // Use any template derived during an earlier call to EscapeSet + // with different top level templates, or clone if necessary. + dt := e.template(dname) + if dt == nil { + dt = template.New(dname) + dt.Tree = &parse.Tree{Name: dname, Root: cloneList(t.Root)} + e.derived[dname] = dt + } + t = dt } - return c, nil + return e.computeOutCtx(c, t), dname } -// tJS is the context transition function for the JS state. -func tJS(c context, s []byte) (context, []byte) { - // TODO: delegate to tSpecialTagEnd to find any </script> once that CL - // has been merged. - - i := bytes.IndexAny(s, `"'/`) - if i == -1 { - // Entire input is non string, comment, regexp tokens. - c.jsCtx = nextJSCtx(s, c.jsCtx) - return c, nil - } - c.jsCtx = nextJSCtx(s[:i], c.jsCtx) - switch s[i] { - case '"': - c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp - case '\'': - c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp - case '/': - switch { - case i+1 < len(s) && s[i+1] == '/': - c.state = stateJSLineCmt - case i+1 < len(s) && s[i+1] == '*': - c.state = stateJSBlockCmt - case c.jsCtx == jsCtxRegexp: - c.state = stateJSRegexp - default: - c.jsCtx = jsCtxRegexp +// computeOutCtx takes a template and its start context and computes the output +// context while storing any inferences in e. +func (e *escaper) computeOutCtx(c context, t *template.Template) context { + // Propagate context over the body. + c1, ok := e.escapeTemplateBody(c, t) + if !ok { + // Look for a fixed point by assuming c1 as the output context. + if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 { + c1, ok = c2, true } - default: - panic("unreachable") + // Use c1 as the error context if neither assumption worked. } - return c, s[i+1:] -} - -// tJSStr is the context transition function for the JS string states. -func tJSStr(c context, s []byte) (context, []byte) { - // TODO: delegate to tSpecialTagEnd to find any </script> once that CL - // has been merged. - - quoteAndEsc := `\"` - if c.state == stateJSSqStr { - quoteAndEsc = `\'` + if !ok && c1.state != stateError { + return context{ + state: stateError, + // TODO: Find the first node with a line in t.Tree.Root + err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()), + } } + return c1 +} - b := s - for { - i := bytes.IndexAny(b, quoteAndEsc) - if i == -1 { - return c, nil +// escapeTemplateBody escapes the given template assuming the given output +// context, and returns the best guess at the output context and whether the +// assumption was correct. +func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) { + filter := func(e1 *escaper, c1 context) bool { + if c1.state == stateError { + // Do not update the input escaper, e. + return false } - if b[i] == '\\' { - i++ - if i == len(b) { - return context{ - state: stateError, - errStr: fmt.Sprintf("unfinished escape sequence in JS string: %q", s), - }, nil - } - } else { - c.state, c.jsCtx = stateJS, jsCtxDivOp - return c, b[i+1:] + if !e1.called[t.Name()] { + // If t is not recursively called, then c1 is an + // accurate output context. + return true } - b = b[i+1:] - } - panic("unreachable") + // c1 is accurate if it matches our assumed output context. + return c.eq(c1) + } + // We need to assume an output context so that recursive template calls + // take the fast path out of escapeTree instead of infinitely recursing. + // Naively assuming that the input context is the same as the output + // works >90% of the time. + e.output[t.Name()] = c + return e.escapeListConditionally(c, t.Tree.Root, filter) } -// tJSRegexp is the context transition function for the /RegExp/ literal state. -func tJSRegexp(c context, s []byte) (context, []byte) { - // TODO: delegate to tSpecialTagEnd to find any </script> once that CL - // has been merged. - - b := s - inCharset := false - for { - i := bytes.IndexAny(b, `/[\]`) - if i == -1 { - break - } - switch b[i] { - case '/': - if !inCharset { - c.state, c.jsCtx = stateJS, jsCtxDivOp - return c, b[i+1:] +// delimEnds maps each delim to a string of characters that terminate it. +var delimEnds = [...]string{ + delimDoubleQuote: `"`, + delimSingleQuote: "'", + // Determined empirically by running the below in various browsers. + // var div = document.createElement("DIV"); + // for (var i = 0; i < 0x10000; ++i) { + // div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>"; + // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0) + // document.write("<p>U+" + i.toString(16)); + // } + delimSpaceOrTagEnd: " \t\n\f\r>", +} + +// escapeText escapes a text template node. +func (e *escaper) escapeText(c context, n *parse.TextNode) context { + s, written, i, b := n.Text, 0, 0, new(bytes.Buffer) + for i != len(s) { + c1, nread := contextAfterText(c, s[i:]) + i1 := i + nread + if c.state == stateText || c.state == stateRCDATA { + end := i1 + if c1.state != c.state { + for j := end - 1; j >= i; j-- { + if s[j] == '<' { + end = j + break + } + } } - case '\\': - i++ - if i == len(b) { - return context{ - state: stateError, - errStr: fmt.Sprintf("unfinished escape sequence in JS regexp: %q", s), - }, nil + for j := i; j < end; j++ { + if s[j] == '<' { + b.Write(s[written:j]) + b.WriteString("<") + written = j + 1 + } } - case '[': - inCharset = true - case ']': - inCharset = false - default: - panic("unreachable") + } else if isComment(c.state) && c.delim == delimNone { + switch c.state { + case stateJSBlockCmt: + // http://es5.github.com/#x7.4: + // "Comments behave like white space and are + // discarded except that, if a MultiLineComment + // contains a line terminator character, then + // the entire comment is considered to be a + // LineTerminator for purposes of parsing by + // the syntactic grammar." + if bytes.IndexAny(s[written:i1], "\n\r\u2028\u2029") != -1 { + b.WriteByte('\n') + } else { + b.WriteByte(' ') + } + case stateCSSBlockCmt: + b.WriteByte(' ') + } + written = i1 + } + if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone { + // Preserve the portion between written and the comment start. + cs := i1 - 2 + if c1.state == stateHTMLCmt { + // "<!--" instead of "/*" or "//" + cs -= 2 + } + b.Write(s[written:cs]) + written = i1 } - b = b[i+1:] + c, i = c1, i1 } - if inCharset { - // This can be fixed by making context richer if interpolation - // into charsets is desired. - return context{ - state: stateError, - errStr: fmt.Sprintf("unfinished JS regexp charset: %q", s), - }, nil + if written != 0 && c.state != stateError { + if !isComment(c.state) || c.delim != delimNone { + b.Write(n.Text[written:]) + } + e.editTextNode(n, b.Bytes()) } - - return c, nil + return c } -var blockCommentEnd = []byte("*/") - -// tJSBlockCmt is the context transition function for the JS /*comment*/ state. -func tJSBlockCmt(c context, s []byte) (context, []byte) { - // TODO: delegate to tSpecialTagEnd to find any </script> once that CL - // has been merged. - - i := bytes.Index(s, blockCommentEnd) - if i == -1 { - return c, nil +// contextAfterText starts in context c, consumes some tokens from the front of +// s, then returns the context after those tokens and the unprocessed suffix. +func contextAfterText(c context, s []byte) (context, int) { + if c.delim == delimNone { + c1, i := tSpecialTagEnd(c, s) + if i == 0 { + // A special end tag (`</script>`) has been seen and + // all content preceding it has been consumed. + return c1, 0 + } + // Consider all content up to any end tag. + return transitionFunc[c.state](c, s[:i]) } - c.state = stateJS - return c, s[i+2:] -} -// tJSLineCmt is the context transition function for the JS //comment state. -func tJSLineCmt(c context, s []byte) (context, []byte) { - // TODO: delegate to tSpecialTagEnd to find any </script> once that CL - // has been merged. - - i := bytes.IndexAny(s, "\r\n\u2028\u2029") + i := bytes.IndexAny(s, delimEnds[c.delim]) if i == -1 { - return c, nil - } - c.state = stateJS - // Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4 - // "However, the LineTerminator at the end of the line is not - // considered to be part of the single-line comment; it is recognised - // separately by the lexical grammar and becomes part of the stream of - // input elements for the syntactic grammar." - return c, s[i:] -} - -// tError is the context transition function for the error state. -func tError(c context, s []byte) (context, []byte) { - return c, nil -} - -// eatAttrName returns the largest j such that s[i:j] is an attribute name. -// It returns an error if s[i:] does not look like it begins with an -// attribute name, such as encountering a quote mark without a preceding -// equals sign. -func eatAttrName(s []byte, i int) (int, os.Error) { - for j := i; j < len(s); j++ { - switch s[j] { - case ' ', '\t', '\n', '\f', '\r', '=', '>': - return j, nil - case '\'', '"', '<': - // These result in a parse warning in HTML5 and are - // indicative of serious problems if seen in an attr - // name in a template. - return 0, fmt.Errorf("%q in attribute name: %.32q", s[j:j+1], s) - default: - // No-op. + // Remain inside the attribute. + // Decode the value so non-HTML rules can easily handle + // <button onclick="alert("Hi!")"> + // without having to entity decode token boundaries. + for u := []byte(html.UnescapeString(string(s))); len(u) != 0; { + c1, i1 := transitionFunc[c.state](c, u) + c, u = c1, u[i1:] } + return c, len(s) } - return len(s), nil + if c.delim != delimSpaceOrTagEnd { + // Consume any quote. + i++ + } + // On exiting an attribute, we discard all state information + // except the state and element. + return context{state: stateTag, element: c.element}, i } -// eatTagName returns the largest j such that s[i:j] is a tag name. -func eatTagName(s []byte, i int) int { - for j := i; j < len(s); j++ { - x := s[j] - switch { - case 'a' <= x && x <= 'z': - // No-op. - case 'A' <= x && x <= 'Z': - // No-op. - case '0' <= x && x <= '9' && i != j: - // No-op. - default: - return j - } +// editActionNode records a change to an action pipeline for later commit. +func (e *escaper) editActionNode(n *parse.ActionNode, cmds []string) { + if _, ok := e.actionNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) } - return len(s) + e.actionNodeEdits[n] = cmds } -// eatWhiteSpace returns the largest j such that s[i:j] is white space. -func eatWhiteSpace(s []byte, i int) int { - for j := i; j < len(s); j++ { - switch s[j] { - case ' ', '\t', '\n', '\f', '\r': - // No-op. - default: - return j - } +// editTemplateNode records a change to a {{template}} callee for later commit. +func (e *escaper) editTemplateNode(n *parse.TemplateNode, callee string) { + if _, ok := e.templateNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) } - return len(s) + e.templateNodeEdits[n] = callee } -// urlAttr is the set of attribute names whose values are URLs. -// It consists of all "%URI"-typed attributes from -// http://www.w3.org/TR/html4/index/attributes.html -// as well as those attributes defined at -// http://dev.w3.org/html5/spec/index.html#attributes-1 -// whose Value column in that table matches -// "Valid [non-empty] URL potentially surrounded by spaces". -var urlAttr = map[string]bool{ - "action": true, - "archive": true, - "background": true, - "cite": true, - "classid": true, - "codebase": true, - "data": true, - "formaction": true, - "href": true, - "icon": true, - "longdesc": true, - "manifest": true, - "poster": true, - "profile": true, - "src": true, - "usemap": true, +// editTextNode records a change to a text node for later commit. +func (e *escaper) editTextNode(n *parse.TextNode, text []byte) { + if _, ok := e.textNodeEdits[n]; ok { + panic(fmt.Sprintf("node %s shared between templates", n)) + } + e.textNodeEdits[n] = text } -// urlFilter returns the HTML equivalent of its input unless it contains an -// unsafe protocol in which case it defangs the entire URL. -func urlFilter(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) +// commit applies changes to actions and template calls needed to contextually +// autoescape content and adds any derived templates to the set. +func (e *escaper) commit() { + for name, _ := range e.output { + e.template(name).Funcs(funcMap) } - if !ok { - s = fmt.Sprint(args...) - } - i := strings.IndexRune(s, ':') - if i >= 0 && strings.IndexRune(s[:i], '/') < 0 { - protocol := strings.ToLower(s[:i]) - if protocol != "http" && protocol != "https" && protocol != "mailto" { - // Return a value that someone investigating a bug - // report can put into a search engine. - return "#ZgotmplZ" - } + for _, t := range e.derived { + e.set.Add(t) + } + for n, s := range e.actionNodeEdits { + ensurePipelineContains(n.Pipe, s) + } + for n, name := range e.templateNodeEdits { + n.Name = name + } + for n, s := range e.textNodeEdits { + n.Text = s + } +} + +// template returns the named template given a mangled template name. +func (e *escaper) template(name string) *template.Template { + t := e.set.Template(name) + if t == nil { + t = e.derived[name] } - // TODO: Once we handle <style>#id { background: url({{.Img}}) }</style> - // we will need to stop this from HTML escaping and pipeline sanitizers. - return template.HTMLEscapeString(s) + return t } diff --git a/src/pkg/exp/template/html/escape_test.go b/src/pkg/exp/template/html/escape_test.go index 6f5ecf6ef..8a64515de 100644 --- a/src/pkg/exp/template/html/escape_test.go +++ b/src/pkg/exp/template/html/escape_test.go @@ -6,6 +6,8 @@ package html import ( "bytes" + "fmt" + "os" "strings" "template" "template/parse" @@ -19,6 +21,7 @@ func TestEscape(t *testing.T) { A, E []string N int Z *int + W HTML }{ F: false, T: true, @@ -29,6 +32,7 @@ func TestEscape(t *testing.T) { E: []string{}, N: 42, Z: nil, + W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`), } tests := []struct { @@ -82,14 +86,9 @@ func TestEscape(t *testing.T) { "true", }, { - // TODO: Make sure the URL escaper escapes single quotes so it can - // be embedded in single quoted URI attributes and CSS url(...) - // constructs. Single quotes are reserved in URLs, but are only used - // in the obsolete "mark" rule in an appendix in RFC 3986 so can be - // safely encoded. "constant", `<a href="/search?q={{"'a<b'"}}">`, - `<a href="/search?q='a%3Cb'">`, + `<a href="/search?q=%27a%3cb%27">`, }, { "multipleAttrs", @@ -122,6 +121,16 @@ func TestEscape(t *testing.T) { `<a href='#ZgotmplZ'>`, }, { + "dangerousURLStart2", + `<a href=' {{"javascript:alert(%22pwned%22)"}}'>`, + `<a href=' #ZgotmplZ'>`, + }, + { + "nonHierURL", + `<a href={{"mailto:Muhammed \"The Greatest\" Ali <m.ali@example.com>"}}>`, + `<a href=mailto:Muhammed "The Greatest" Ali <m.ali@example.com>>`, + }, + { "urlPath", `<a href='http://{{"javascript:80"}}/foo'>`, `<a href='http://javascript:80/foo'>`, @@ -129,12 +138,12 @@ func TestEscape(t *testing.T) { { "urlQuery", `<a href='/search?q={{.H}}'>`, - `<a href='/search?q=%3CHello%3E'>`, + `<a href='/search?q=%3cHello%3e'>`, }, { "urlFragment", `<a href='/faq#{{.H}}'>`, - `<a href='/faq#%3CHello%3E'>`, + `<a href='/faq#%3cHello%3e'>`, }, { "urlBranch", @@ -144,7 +153,7 @@ func TestEscape(t *testing.T) { { "urlBranchConflictMoot", `<a href="{{if .T}}/foo?a={{else}}/bar#{{end}}{{.C}}">`, - `<a href="/foo?a=%3CCincinatti%3E">`, + `<a href="/foo?a=%3cCincinatti%3e">`, }, { "jsStrValue", @@ -172,6 +181,11 @@ func TestEscape(t *testing.T) { `<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`, }, { + "jsObjValueScript", + "<script>alert({{.A}})</script>", + `<script>alert(["\u003ca\u003e","\u003cb\u003e"])</script>`, + }, + { "jsObjValueNotOverEscaped", "<button onclick='alert({{.A | html}})'>", `<button onclick='alert(["\u003ca\u003e","\u003cb\u003e"])'>`, @@ -189,16 +203,374 @@ func TestEscape(t *testing.T) { }, { "jsRe", - "<button onclick='alert("{{.H}}")'>", - `<button onclick='alert("\x3cHello\x3e")'>`, + `<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`, + `<button onclick='alert(/foo\x2bbar/.test(""))'>`, + }, + { + "jsReBlank", + `<script>alert(/{{""}}/.test(""));</script>`, + `<script>alert(/(?:)/.test(""));</script>`, + }, + { + "jsReAmbigOk", + `<script>{{if true}}var x = 1{{end}}</script>`, + // The {if} ends in an ambiguous jsCtx but there is + // no slash following so we shouldn't care. + `<script>var x = 1</script>`, + }, + { + "styleBidiKeywordPassed", + `<p style="dir: {{"ltr"}}">`, + `<p style="dir: ltr">`, + }, + { + "styleBidiPropNamePassed", + `<p style="border-{{"left"}}: 0; border-{{"right"}}: 1in">`, + `<p style="border-left: 0; border-right: 1in">`, + }, + { + "styleExpressionBlocked", + `<p style="width: {{"expression(alert(1337))"}}">`, + `<p style="width: ZgotmplZ">`, + }, + { + "styleTagSelectorPassed", + `<style>{{"p"}} { color: pink }</style>`, + `<style>p { color: pink }</style>`, + }, + { + "styleIDPassed", + `<style>p{{"#my-ID"}} { font: Arial }</style>`, + `<style>p#my-ID { font: Arial }</style>`, + }, + { + "styleClassPassed", + `<style>p{{".my_class"}} { font: Arial }</style>`, + `<style>p.my_class { font: Arial }</style>`, + }, + { + "styleQuantityPassed", + `<a style="left: {{"2em"}}; top: {{0}}">`, + `<a style="left: 2em; top: 0">`, + }, + { + "stylePctPassed", + `<table style=width:{{"100%"}}>`, + `<table style=width:100%>`, + }, + { + "styleColorPassed", + `<p style="color: {{"#8ff"}}; background: {{"#000"}}">`, + `<p style="color: #8ff; background: #000">`, + }, + { + "styleObfuscatedExpressionBlocked", + `<p style="width: {{" e\78preS\0Sio/**/n(alert(1337))"}}">`, + `<p style="width: ZgotmplZ">`, + }, + { + "styleMozBindingBlocked", + `<p style="{{"-moz-binding(alert(1337))"}}: ...">`, + `<p style="ZgotmplZ: ...">`, + }, + { + "styleObfuscatedMozBindingBlocked", + `<p style="{{" -mo\7a-B\0I/**/nding(alert(1337))"}}: ...">`, + `<p style="ZgotmplZ: ...">`, + }, + { + "styleFontNameString", + `<p style='font-family: "{{"Times New Roman"}}"'>`, + `<p style='font-family: "Times New Roman"'>`, + }, + { + "styleFontNameString", + `<p style='font-family: "{{"Times New Roman"}}", "{{"sans-serif"}}"'>`, + `<p style='font-family: "Times New Roman", "sans-serif"'>`, + }, + { + "styleFontNameUnquoted", + `<p style='font-family: {{"Times New Roman"}}'>`, + `<p style='font-family: Times New Roman'>`, + }, + { + "styleURLQueryEncoded", + `<p style="background: url(/img?name={{"O'Reilly Animal(1)<2>.png"}})">`, + `<p style="background: url(/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png)">`, + }, + { + "styleQuotedURLQueryEncoded", + `<p style="background: url('/img?name={{"O'Reilly Animal(1)<2>.png"}}')">`, + `<p style="background: url('/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png')">`, + }, + { + "styleStrQueryEncoded", + `<p style="background: '/img?name={{"O'Reilly Animal(1)<2>.png"}}'">`, + `<p style="background: '/img?name=O%27Reilly%20Animal%281%29%3c2%3e.png'">`, + }, + { + "styleURLBadProtocolBlocked", + `<a style="background: url('{{"javascript:alert(1337)"}}')">`, + `<a style="background: url('#ZgotmplZ')">`, + }, + { + "styleStrBadProtocolBlocked", + `<a style="background: '{{"javascript:alert(1337)"}}'">`, + `<a style="background: '#ZgotmplZ'">`, + }, + { + "styleURLGoodProtocolPassed", + `<a style="background: url('{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}')">`, + `<a style="background: url('http://oreilly.com/O%27Reilly%20Animals%281%29%3c2%3e;%7b%7d.html')">`, + }, + { + "styleStrGoodProtocolPassed", + `<a style="background: '{{"http://oreilly.com/O'Reilly Animals(1)<2>;{}.html"}}'">`, + `<a style="background: 'http\3a\2f\2foreilly.com\2fO\27Reilly Animals\28 1\29\3c 2\3e\3b\7b\7d.html'">`, + }, + { + "styleURLEncodedForHTMLInAttr", + `<a style="background: url('{{"/search?img=foo&size=icon"}}')">`, + `<a style="background: url('/search?img=foo&size=icon')">`, + }, + { + "styleURLNotEncodedForHTMLInCdata", + `<style>body { background: url('{{"/search?img=foo&size=icon"}}') }</style>`, + `<style>body { background: url('/search?img=foo&size=icon') }</style>`, + }, + { + "styleURLMixedCase", + `<p style="background: URL(#{{.H}})">`, + `<p style="background: URL(#%3cHello%3e)">`, + }, + { + "stylePropertyPairPassed", + `<a style='{{"color: red"}}'>`, + `<a style='color: red'>`, + }, + { + "styleStrSpecialsEncoded", + `<a style="font-family: '{{"/**/'\";:// \\"}}', "{{"/**/'\";:// \\"}}"">`, + `<a style="font-family: '\2f**\2f\27\22\3b\3a\2f\2f \\', "\2f**\2f\27\22\3b\3a\2f\2f \\"">`, + }, + { + "styleURLSpecialsEncoded", + // TODO: Find out what IE does with url(/*foo*/bar) + // FF, Chrome, and Safari seem to treat it as a URL. + `<a style="border-image: url({{"/**/'\";:// \\"}}), url("{{"/**/'\";:// \\"}}"), url('{{"/**/'\";:// \\"}}'), 'http://www.example.com/?q={{"/**/'\";:// \\"}}''">`, + `<a style="border-image: url(/**/%27%22;://%20%5c), url("/**/%27%22;://%20%5c"), url('/**/%27%22;://%20%5c'), 'http://www.example.com/?q=%2f%2a%2a%2f%27%22%3b%3a%2f%2f%20%5c''">`, + }, + { + "HTML comment", + "<b>Hello, <!-- name of world -->{{.C}}</b>", + "<b>Hello, <Cincinatti></b>", + }, + { + "HTML comment not first < in text node.", + "<<!-- -->!--", + "<!--", + }, + { + "HTML normalization 1", + "a < b", + "a < b", + }, + { + "HTML normalization 2", + "a << b", + "a << b", + }, + { + "HTML normalization 3", + "a<<!-- --><!-- -->b", + "a<b", + }, + { + "Split HTML comment", + "<b>Hello, <!-- name of {{if .T}}city -->{{.C}}{{else}}world -->{{.W}}{{end}}</b>", + "<b>Hello, <Cincinatti></b>", + }, + { + "JS line comment", + "<script>for (;;) { if (c()) break// foo not a label\n" + + "foo({{.T}});}</script>", + "<script>for (;;) { if (c()) break\n" + + "foo( true );}</script>", + }, + { + "JS multiline block comment", + "<script>for (;;) { if (c()) break/* foo not a label\n" + + " */foo({{.T}});}</script>", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "<script>for (;;) { if (c()) break\n" + + "foo( true );}</script>", + }, + { + "JS single-line block comment", + "<script>for (;;) {\n" + + "if (c()) break/* foo a label */foo;" + + "x({{.T}});}</script>", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "<script>for (;;) {\n" + + "if (c()) break foo;" + + "x( true );}</script>", + }, + { + "JS block comment flush with mathematical division", + "<script>var a/*b*//c\nd</script>", + "<script>var a /c\nd</script>", + }, + { + "JS mixed comments", + "<script>var a/*b*///c\nd</script>", + "<script>var a \nd</script>", + }, + { + "CSS comments", + "<style>p// paragraph\n" + + `{border: 1px/* color */{{"#00f"}}}</style>`, + "<style>p\n" + + "{border: 1px #00f}</style>", + }, + { + "JS attr block comment", + `<a onclick="f(""); /* alert({{.H}}) */">`, + // Attribute comment tests should pass if the comments + // are successfully elided. + `<a onclick="f(""); /* alert() */">`, + }, + { + "JS attr line comment", + `<a onclick="// alert({{.G}})">`, + `<a onclick="// alert()">`, + }, + { + "CSS attr block comment", + `<a style="/* color: {{.H}} */">`, + `<a style="/* color: */">`, + }, + { + "CSS attr line comment", + `<a style="// color: {{.G}}">`, + `<a style="// color: ">`, + }, + { + "HTML substitution commented out", + "<p><!-- {{.H}} --></p>", + "<p></p>", + }, + { + "Comment ends flush with start", + "<!--{{.}}--><script>/*{{.}}*///{{.}}\n</script><style>/*{{.}}*///{{.}}\n</style><a onclick='/*{{.}}*///{{.}}' style='/*{{.}}*///{{.}}'>", + "<script> \n</script><style> \n</style><a onclick='/**///' style='/**///'>", + }, + { + "typed HTML in text", + `{{.W}}`, + `¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`, + }, + { + "typed HTML in attribute", + `<div title="{{.W}}">`, + `<div title="¡Hello, O'World!">`, + }, + { + "typed HTML in script", + `<button onclick="alert({{.W}})">`, + `<button onclick="alert("&iexcl;\u003cb class=\"foo\"\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO'World\u003c/textarea\u003e!")">`, + }, + { + "typed HTML in RCDATA", + `<textarea>{{.W}}</textarea>`, + `<textarea>¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!</textarea>`, + }, + { + "range in textarea", + "<textarea>{{range .A}}{{.}}{{end}}</textarea>", + "<textarea><a><b></textarea>", + }, + { + "auditable exemption from escaping", + "{{range .A}}{{. | noescape}}{{end}}", + "<a><b>", + }, + { + "No tag injection", + `{{"10$"}}<{{"script src,evil.org/pwnd.js"}}...`, + `10$<script src,evil.org/pwnd.js...`, + }, + { + "No comment injection", + `<{{"!--"}}`, + `<!--`, + }, + { + "No RCDATA end tag injection", + `<textarea><{{"/textarea "}}...</textarea>`, + `<textarea></textarea ...</textarea>`, + }, + { + "optional attrs", + `<img class="{{"iconClass"}}"` + + `{{if .T}} id="{{"<iconId>"}}"{{end}}` + + // Double quotes inside if/else. + ` src=` + + `{{if .T}}"?{{"<iconPath>"}}"` + + `{{else}}"images/cleardot.gif"{{end}}` + + // Missing space before title, but it is not a + // part of the src attribute. + `{{if .T}}title="{{"<title>"}}"{{end}}` + + // Quotes outside if/else. + ` alt="` + + `{{if .T}}{{"<alt>"}}` + + `{{else}}{{if .F}}{{"<title>"}}{{end}}` + + `{{end}}"` + + `>`, + `<img class="iconClass" id="<iconId>" src="?%3ciconPath%3e"title="<title>" alt="<alt>">`, + }, + { + "conditional valueless attr name", + `<input{{if .T}} checked{{end}} name=n>`, + `<input checked name=n>`, + }, + { + "conditional dynamic valueless attr name 1", + `<input{{if .T}} {{"checked"}}{{end}} name=n>`, + `<input checked name=n>`, + }, + { + "conditional dynamic valueless attr name 2", + `<input {{if .T}}{{"checked"}} {{end}}name=n>`, + `<input checked name=n>`, + }, + { + "dynamic attribute name", + `<img on{{"load"}}="alert({{"loaded"}})">`, + // Treated as JS since quotes are inserted. + `<img onload="alert("loaded")">`, + }, + { + "dynamic element name", + `<h{{3}}><table><t{{"head"}}>...</h{{3}}>`, + `<h3><table><thead>...</h3>`, }, } for _, test := range tests { - tmpl := template.Must(template.New(test.name).Parse(test.input)) - tmpl, err := Escape(tmpl) + tmpl := template.New(test.name) + // TODO: Move noescape into template/func.go + tmpl.Funcs(template.FuncMap{ + "noescape": func(a ...interface{}) string { + return fmt.Sprint(a...) + }, + }) + tmpl = template.Must(Escape(template.Must(tmpl.Parse(test.input)))) b := new(bytes.Buffer) - if err = tmpl.Execute(b, data); err != nil { + if err := tmpl.Execute(b, data); err != nil { t.Errorf("%s: template execution failed: %s", test.name, err) continue } @@ -209,6 +581,151 @@ func TestEscape(t *testing.T) { } } +func TestEscapeSet(t *testing.T) { + type dataItem struct { + Children []*dataItem + X string + } + + data := dataItem{ + Children: []*dataItem{ + &dataItem{X: "foo"}, + &dataItem{X: "<bar>"}, + &dataItem{ + Children: []*dataItem{ + &dataItem{X: "baz"}, + }, + }, + }, + } + + tests := []struct { + inputs map[string]string + want string + }{ + // The trivial set. + { + map[string]string{ + "main": ``, + }, + ``, + }, + // A template called in the start context. + { + map[string]string{ + "main": `Hello, {{template "helper"}}!`, + // Not a valid top level HTML template. + // "<b" is not a full tag. + "helper": `{{"<World>"}}`, + }, + `Hello, <World>!`, + }, + // A template called in a context other than the start. + { + map[string]string{ + "main": `<a onclick='a = {{template "helper"}};'>`, + // Not a valid top level HTML template. + // "<b" is not a full tag. + "helper": `{{"<a>"}}<b`, + }, + `<a onclick='a = "\u003ca\u003e"<b;'>`, + }, + // A recursive template that ends in its start context. + { + map[string]string{ + "main": `{{range .Children}}{{template "main" .}}{{else}}{{.X}} {{end}}`, + }, + `foo <bar> baz `, + }, + // A recursive helper template that ends in its start context. + { + map[string]string{ + "main": `{{template "helper" .}}`, + "helper": `{{if .Children}}<ul>{{range .Children}}<li>{{template "main" .}}</li>{{end}}</ul>{{else}}{{.X}}{{end}}`, + }, + `<ul><li>foo</li><li><bar></li><li><ul><li>baz</li></ul></li></ul>`, + }, + // Co-recursive templates that end in its start context. + { + map[string]string{ + "main": `<blockquote>{{range .Children}}{{template "helper" .}}{{end}}</blockquote>`, + "helper": `{{if .Children}}{{template "main" .}}{{else}}{{.X}}<br>{{end}}`, + }, + `<blockquote>foo<br><bar><br><blockquote>baz<br></blockquote></blockquote>`, + }, + // A template that is called in two different contexts. + { + map[string]string{ + "main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`, + "helper": `{{11}} of {{"<100>"}}`, + }, + `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`, + }, + // A non-recursive template that ends in a different context. + // helper starts in jsCtxRegexp and ends in jsCtxDivOp. + { + map[string]string{ + "main": `<script>var x={{template "helper"}}/{{"42"}};</script>`, + "helper": "{{126}}", + }, + `<script>var x= 126 /"42";</script>`, + }, + // A recursive template that ends in a similar context. + { + map[string]string{ + "main": `<script>var x=[{{template "countdown" 4}}];</script>`, + "countdown": `{{.}}{{if .}},{{template "countdown" . | pred}}{{end}}`, + }, + `<script>var x=[ 4 , 3 , 2 , 1 , 0 ];</script>`, + }, + // A recursive template that ends in a different context. + /* + { + map[string]string{ + "main": `<a href="/foo{{template "helper" .}}">`, + "helper": `{{if .Children}}{{range .Children}}{{template "helper" .}}{{end}}{{else}}?x={{.X}}{{end}}`, + }, + `<a href="/foo?x=foo?x=%3cbar%3e?x=baz">`, + }, + */ + } + + // pred is a template function that returns the predecessor of a + // natural number for testing recursive templates. + fns := template.FuncMap{"pred": func(a ...interface{}) (interface{}, os.Error) { + if len(a) == 1 { + if i, _ := a[0].(int); i > 0 { + return i - 1, nil + } + } + return nil, fmt.Errorf("undefined pred(%v)", a) + }} + + for _, test := range tests { + var s template.Set + for name, src := range test.inputs { + t := template.New(name) + t.Funcs(fns) + s.Add(template.Must(t.Parse(src))) + } + s.Funcs(fns) + if _, err := EscapeSet(&s, "main"); err != nil { + t.Errorf("%s for input:\n%v", err, test.inputs) + continue + } + var b bytes.Buffer + + if err := s.Execute(&b, "main", data); err != nil { + t.Errorf("%q executing %v", err.String(), s.Template("main")) + continue + } + if got := b.String(); test.want != got { + t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) + } + } + +} + func TestErrors(t *testing.T) { tests := []struct { input string @@ -276,7 +793,11 @@ func TestErrors(t *testing.T) { }, { "<a b=1 c={{.H}}", - "z ends in a non-text context: {stateAttr delimSpaceOrTagEnd", + "z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd", + }, + { + "<script>foo();", + "z: ends in a non-text context: {stateJS", }, { `<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`, @@ -292,26 +813,55 @@ func TestErrors(t *testing.T) { }, { `<a onclick='alert(/x+\`, - `unfinished escape sequence in JS regexp: "x+\\"`, + `unfinished escape sequence in JS string: "x+\\"`, }, { `<a onclick="/foo[\]/`, `unfinished JS regexp charset: "foo[\\]/"`, }, { - `<a onclick="/* alert({{.X}} */">`, - `z:1: (action: [(command: [F=[X]])]) appears inside a comment`, + // It is ambiguous whether 1.5 should be 1\.5 or 1.5. + // Either `var x = 1/- 1.5 /i.test(x)` + // where `i.test(x)` is a method call of reference i, + // or `/-1\.5/i.test(x)` which is a method call on a + // case insensitive regular expression. + `<script>{{if false}}var x = 1{{end}}/-{{"1.5"}}/i.test(x)</script>`, + `'/' could start div or regexp: "/-"`, }, { - `<a onclick="// alert({{.X}}">`, - `z:1: (action: [(command: [F=[X]])]) appears inside a comment`, + `{{template "foo"}}`, + "z:1: no such template foo", + }, + { + `{{define "z"}}<div{{template "y"}}>{{end}}` + + // Illegal starting in stateTag but not in stateText. + `{{define "y"}} foo<b{{end}}`, + `"<" in attribute name: " foo<b"`, + }, + { + `{{define "z"}}<script>reverseList = [{{template "t"}}]</script>{{end}}` + + // Missing " after recursive call. + `{{define "t"}}{{if .Tail}}{{template "t" .Tail}}{{end}}{{.Head}}",{{end}}`, + `: cannot compute output context for template t$htmltemplate_stateJS_elementScript`, }, } for _, test := range tests { - tmpl := template.Must(template.New("z").Parse(test.input)) + var err os.Error + if strings.HasPrefix(test.input, "{{define") { + var s template.Set + _, err = s.Parse(test.input) + if err != nil { + t.Errorf("Failed to parse %q: %s", test.input, err) + continue + } + _, err = EscapeSet(&s, "z") + } else { + tmpl := template.Must(template.New("z").Parse(test.input)) + _, err = Escape(tmpl) + } var got string - if _, err := Escape(tmpl); err != nil { + if err != nil { got = err.String() } if test.err == "" { @@ -321,7 +871,7 @@ func TestErrors(t *testing.T) { continue } if strings.Index(got, test.err) == -1 { - t.Errorf("input=%q: error %q does not contain expected string %q", test.input, got, test.err) + t.Errorf("input=%q: error\n\t%q\ndoes not contain expected string\n\t%q", test.input, got, test.err) continue } } @@ -358,8 +908,24 @@ func TestEscapeText(t *testing.T) { context{state: stateText}, }, { + `<a href`, + context{state: stateAttrName, attr: attrURL}, + }, + { + `<a on`, + context{state: stateAttrName, attr: attrScript}, + }, + { + `<a href `, + context{state: stateAfterName, attr: attrURL}, + }, + { + `<a style = `, + context{state: stateBeforeValue, attr: attrStyle}, + }, + { `<a href=`, - context{state: stateURL, delim: delimSpaceOrTagEnd}, + context{state: stateBeforeValue, attr: attrURL}, }, { `<a href=x`, @@ -470,6 +1036,14 @@ func TestEscapeText(t *testing.T) { context{state: stateJSBlockCmt, delim: delimDoubleQuote}, }, { + `<a onclick="/*/`, + context{state: stateJSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a onclick="/**/`, + context{state: stateJS, delim: delimDoubleQuote}, + }, + { `<a onkeypress=""`, context{state: stateJSDqStr, delim: delimDoubleQuote}, }, @@ -518,6 +1092,10 @@ func TestEscapeText(t *testing.T) { context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, }, { + `<script>/foo/ /=`, + context{state: stateJS, element: elementScript}, + }, + { `<a onclick="1 /foo`, context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, }, @@ -533,11 +1111,191 @@ func TestEscapeText(t *testing.T) { `<a onclick="/foo\/`, context{state: stateJSRegexp, delim: delimDoubleQuote}, }, + { + `<a onclick="/foo/`, + context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp}, + }, + { + `<input checked style="`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="//`, + context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + }, + { + `<a style="//</script>`, + context{state: stateCSSLineCmt, delim: delimDoubleQuote}, + }, + { + "<a style='//\n", + context{state: stateCSS, delim: delimSingleQuote}, + }, + { + "<a style='//\r", + context{state: stateCSS, delim: delimSingleQuote}, + }, + { + `<a style="/*`, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a style="/*/`, + context{state: stateCSSBlockCmt, delim: delimDoubleQuote}, + }, + { + `<a style="/**/`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: '`, + context{state: stateCSSSqStr, delim: delimDoubleQuote}, + }, + { + `<a style="background: "`, + context{state: stateCSSDqStr, delim: delimDoubleQuote}, + }, + { + `<a style="background: '/foo?img=`, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + }, + { + `<a style="background: '/`, + context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url("/`, + context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/`, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/)`, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url('/ `, + context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url(/`, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery}, + }, + { + `<a style="background: url( `, + context{state: stateCSSURL, delim: delimDoubleQuote}, + }, + { + `<a style="background: url( /image?name=`, + context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag}, + }, + { + `<a style="background: url(x)`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: url('x'`, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<a style="background: url( x `, + context{state: stateCSS, delim: delimDoubleQuote}, + }, + { + `<!-- foo`, + context{state: stateHTMLCmt}, + }, + { + `<!-->`, + context{state: stateHTMLCmt}, + }, + { + `<!--->`, + context{state: stateHTMLCmt}, + }, + { + `<!-- foo -->`, + context{state: stateText}, + }, + { + `<script`, + context{state: stateTag, element: elementScript}, + }, + { + `<script `, + context{state: stateTag, element: elementScript}, + }, + { + `<script src="foo.js" `, + context{state: stateTag, element: elementScript}, + }, + { + `<script src='foo.js' `, + context{state: stateTag, element: elementScript}, + }, + { + `<script type=text/javascript `, + context{state: stateTag, element: elementScript}, + }, + { + `<script>foo`, + context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript}, + }, + { + `<script>foo</script>`, + context{state: stateText}, + }, + { + `<script>foo</script><!--`, + context{state: stateHTMLCmt}, + }, + { + `<script>document.write("<p>foo</p>");`, + context{state: stateJS, element: elementScript}, + }, + { + `<script>document.write("<p>foo<\/script>");`, + context{state: stateJS, element: elementScript}, + }, + { + `<script>document.write("<script>alert(1)</script>");`, + context{state: stateText}, + }, + { + `<Script>`, + context{state: stateJS, element: elementScript}, + }, + { + `<SCRIPT>foo`, + context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript}, + }, + { + `<textarea>value`, + context{state: stateRCDATA, element: elementTextarea}, + }, + { + `<textarea>value</TEXTAREA>`, + context{state: stateText}, + }, + { + `<textarea name=html><b`, + context{state: stateRCDATA, element: elementTextarea}, + }, + { + `<title>value`, + context{state: stateRCDATA, element: elementTitle}, + }, + { + `<style>value`, + context{state: stateCSS, element: elementStyle}, + }, } for _, test := range tests { - b := []byte(test.input) - c := escapeText(context{}, b) + b, e := []byte(test.input), newEscaper(nil) + c := e.escapeText(context{}, &parse.TextNode{parse.NodeText, b}) if !test.output.eq(c) { t.Errorf("input %q: want context\n\t%v\ngot\n\t%v", test.input, test.output, c) continue @@ -615,3 +1373,32 @@ func TestEnsurePipelineContains(t *testing.T) { } } } + +func expectExecuteFailure(t *testing.T, b *bytes.Buffer) { + if x := recover(); x != nil { + if b.Len() != 0 { + t.Errorf("output on buffer: %q", b.String()) + } + } else { + t.Errorf("unescaped template executed") + } +} + +func TestEscapeErrorsNotIgnorable(t *testing.T) { + var b bytes.Buffer + tmpl := template.Must(template.New("dangerous").Parse("<a")) + Escape(tmpl) + defer expectExecuteFailure(t, &b) + tmpl.Execute(&b, nil) +} + +func TestEscapeSetErrorsNotIgnorable(t *testing.T) { + s, err := (&template.Set{}).Parse(`{{define "t"}}<a{{end}}`) + if err != nil { + t.Error("failed to parse set: %q", err) + } + EscapeSet(s, "t") + var b bytes.Buffer + defer expectExecuteFailure(t, &b) + s.Execute(&b, "t", nil) +} diff --git a/src/pkg/exp/template/html/html.go b/src/pkg/exp/template/html/html.go new file mode 100644 index 000000000..3924b193d --- /dev/null +++ b/src/pkg/exp/template/html/html.go @@ -0,0 +1,243 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "fmt" + "utf8" +) + +// htmlNospaceEscaper escapes for inclusion in unquoted attribute values. +func htmlNospaceEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false) + } + return htmlReplacer(s, htmlNospaceReplacementTable, false) +} + +// attrEscaper escapes for inclusion in quoted attribute values. +func attrEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(stripTags(s), htmlNormReplacementTable, true) + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// rcdataEscaper escapes for inclusion in an RCDATA element body. +func rcdataEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return htmlReplacer(s, htmlNormReplacementTable, true) + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// htmlEscaper escapes for inclusion in HTML text. +func htmlEscaper(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTML { + return s + } + return htmlReplacer(s, htmlReplacementTable, true) +} + +// htmlReplacementTable contains the runes that need to be escaped +// inside a quoted attribute value or in a text node. +var htmlReplacementTable = []string{ + // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state: " + // U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT + // CHARACTER character to the current attribute's value. + // " + // and similarly + // http://www.w3.org/TR/html5/tokenization.html#before-attribute-value-state + 0: "\uFFFD", + '"': """, + '&': "&", + '\'': "'", + '+': "+", + '<': "<", + '>': ">", +} + +// htmlNormReplacementTable is like htmlReplacementTable but without '&' to +// avoid over-encoding existing entities. +var htmlNormReplacementTable = []string{ + 0: "\uFFFD", + '"': """, + '\'': "'", + '+': "+", + '<': "<", + '>': ">", +} + +// htmlNospaceReplacementTable contains the runes that need to be escaped +// inside an unquoted attribute value. +// The set of runes escaped is the union of the HTML specials and +// those determined by running the JS below in browsers: +// <div id=d></div> +// <script>(function () { +// var a = [], d = document.getElementById("d"), i, c, s; +// for (i = 0; i < 0x10000; ++i) { +// c = String.fromCharCode(i); +// d.innerHTML = "<span title=" + c + "lt" + c + "></span>" +// s = d.getElementsByTagName("SPAN")[0]; +// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); } +// } +// document.write(a.join(", ")); +// })()</script> +var htmlNospaceReplacementTable = []string{ + 0: "�", + '\t': "	", + '\n': " ", + '\v': "", + '\f': "", + '\r': " ", + ' ': " ", + '"': """, + '&': "&", + '\'': "'", + '+': "+", + '<': "<", + '=': "=", + '>': ">", + // A parse error in the attribute value (unquoted) and + // before attribute value states. + // Treated as a quoting character by IE. + '`': "`", +} + +// htmlNospaceNormReplacementTable is like htmlNospaceReplacementTable but +// without '&' to avoid over-encoding existing entities. +var htmlNospaceNormReplacementTable = []string{ + 0: "�", + '\t': "	", + '\n': " ", + '\v': "", + '\f': "", + '\r': " ", + ' ': " ", + '"': """, + '\'': "'", + '+': "+", + '<': "<", + '=': "=", + '>': ">", + // A parse error in the attribute value (unquoted) and + // before attribute value states. + // Treated as a quoting character by IE. + '`': "`", +} + +// htmlReplacer returns s with runes replaced acccording to replacementTable +// and when badRunes is true, certain bad runes are allowed through unescaped. +func htmlReplacer(s string, replacementTable []string, badRunes bool) string { + written, b := 0, new(bytes.Buffer) + for i, r := range s { + if r < len(replacementTable) { + if repl := replacementTable[r]; len(repl) != 0 { + b.WriteString(s[written:i]) + b.WriteString(repl) + // Valid as long as replacementTable doesn't + // include anything above 0x7f. + written = i + utf8.RuneLen(r) + } + } else if badRunes { + // No-op. + // IE does not allow these ranges in unquoted attrs. + } else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff { + fmt.Fprintf(b, "%s&#x%x;", s[written:i], r) + written = i + utf8.RuneLen(r) + } + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} + +// stripTags takes a snippet of HTML and returns only the text content. +// For example, `<b>¡Hi!</b> <script>...</script>` -> `¡Hi! `. +func stripTags(html string) string { + var b bytes.Buffer + s, c, i, allText := []byte(html), context{}, 0, true + // Using the transition funcs helps us avoid mangling + // `<div title="1>2">` or `I <3 Ponies!`. + for i != len(s) { + if c.delim == delimNone { + st := c.state + // Use RCDATA instead of parsing into JS or CSS styles. + if c.element != elementNone && !isInTag(st) { + st = stateRCDATA + } + d, nread := transitionFunc[st](c, s[i:]) + i1 := i + nread + if c.state == stateText || c.state == stateRCDATA { + // Emit text up to the start of the tag or comment. + j := i1 + if d.state != c.state { + for j1 := j - 1; j1 >= i; j1-- { + if s[j1] == '<' { + j = j1 + break + } + } + } + b.Write(s[i:j]) + } else { + allText = false + } + c, i = d, i1 + continue + } + i1 := i + bytes.IndexAny(s[i:], delimEnds[c.delim]) + if i1 < i { + break + } + if c.delim != delimSpaceOrTagEnd { + // Consume any quote. + i1++ + } + c, i = context{state: stateTag, element: c.element}, i1 + } + if allText { + return html + } else if c.state == stateText || c.state == stateRCDATA { + b.Write(s[i:]) + } + return b.String() +} + +// htmlNameFilter accepts valid parts of an HTML attribute or tag name or +// a known-safe HTML attribute. +func htmlNameFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeHTMLAttr { + return s + } + for _, r := range s { + switch { + case '0' <= r && r <= '9': + case 'A' <= r && r <= 'Z': + case 'a' <= r && r <= 'z': + default: + return filterFailsafe + } + } + return s +} + +// commentEscaper returns the empty string regardless of input. +// Comment content does not correspond to any parsed structure or +// human-readable content, so the simplest and most secure policy is to drop +// content interpolated into comments. +// This approach is equally valid whether or not static comment content is +// removed from the template. +func commentEscaper(args ...interface{}) string { + return "" +} diff --git a/src/pkg/exp/template/html/html_test.go b/src/pkg/exp/template/html/html_test.go new file mode 100644 index 000000000..e178d0f27 --- /dev/null +++ b/src/pkg/exp/template/html/html_test.go @@ -0,0 +1,94 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "html" + "strings" + "testing" +) + +func TestHTMLNospaceEscaper(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E") + + want := ("�\x01\x02\x03\x04\x05\x06\x07" + + "\x08	  \x0E\x0F" + + "\x10\x11\x12\x13\x14\x15\x16\x17" + + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + ``abcdefghijklmno` + + `pqrstuvwxyz{|}~` + "\u007f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + got := htmlNospaceEscaper(input) + if got != want { + t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got) + } + + got, want = html.UnescapeString(got), strings.Replace(input, "\x00", "\ufffd", 1) + if want != got { + t.Errorf("decode: want\n\t%q\nbut got\n\t%q", want, got) + } +} + +func TestStripTags(t *testing.T) { + tests := []struct { + input, want string + }{ + {"", ""}, + {"Hello, World!", "Hello, World!"}, + {"foo&bar", "foo&bar"}, + {`Hello <a href="www.example.com/">World</a>!`, "Hello World!"}, + {"Foo <textarea>Bar</textarea> Baz", "Foo Bar Baz"}, + {"Foo <!-- Bar --> Baz", "Foo Baz"}, + {"<", "<"}, + {"foo < bar", "foo < bar"}, + {`Foo<script type="text/javascript">alert(1337)</script>Bar`, "FooBar"}, + {`Foo<div title="1>2">Bar`, "FooBar"}, + {`I <3 Ponies!`, `I <3 Ponies!`}, + {`<script>foo()</script>`, ``}, + } + + for _, test := range tests { + if got := stripTags(test.input); got != test.want { + t.Errorf("%q: want %q, got %q", test.input, test.want, got) + } + } +} + +func BenchmarkHTMLNospaceEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + htmlNospaceEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkHTMLNospaceEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + htmlNospaceEscaper("The_quick,_brown_fox_jumps_over_the_lazy_dog.") + } +} + +func BenchmarkStripTags(b *testing.B) { + for i := 0; i < b.N; i++ { + stripTags("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") + } +} + +func BenchmarkStripTagsNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + stripTags("The quick, brown fox jumps over the lazy dog.") + } +} diff --git a/src/pkg/exp/template/html/js.go b/src/pkg/exp/template/html/js.go index 65479bc13..4318b00ac 100644 --- a/src/pkg/exp/template/html/js.go +++ b/src/pkg/exp/template/html/js.go @@ -123,6 +123,17 @@ func jsValEscaper(args ...interface{}) string { var a interface{} if len(args) == 1 { a = args[0] + switch t := a.(type) { + case JS: + return string(t) + case JSStr: + // TODO: normalize quotes. + return `"` + string(t) + `"` + case json.Marshaler: + // Do not treat as a Stringer. + case fmt.Stringer: + a = t.String() + } } else { a = fmt.Sprint(args...) } @@ -166,7 +177,11 @@ func jsValEscaper(args ...interface{}) string { // JavaScript source, in JavaScript embedded in an HTML5 <script> element, // or in an HTML5 event handler attribute such as onclick. func jsStrEscaper(args ...interface{}) string { - return replace(stringify(args...), jsStrReplacementTable) + s, t := stringify(args...) + if t == contentTypeJSStr { + return replace(s, jsStrNormReplacementTable) + } + return replace(s, jsStrReplacementTable) } // jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression @@ -174,24 +189,20 @@ func jsStrEscaper(args ...interface{}) string { // expression literal. /foo{{.X}}bar/ matches the string "foo" followed by // the literal text of {{.X}} followed by the string "bar". func jsRegexpEscaper(args ...interface{}) string { - return replace(stringify(args...), jsRegexpReplacementTable) -} - -// stringify is an optimized form of fmt.Sprint. -func stringify(args ...interface{}) string { - if len(args) == 1 { - if s, ok := args[0].(string); ok { - return s - } + s, _ := stringify(args...) + s = replace(s, jsRegexpReplacementTable) + if s == "" { + // /{{.X}}/ should not produce a line comment when .X == "". + return "(?:)" } - return fmt.Sprint(args...) + return s } // replace replaces each rune r of s with replacementTable[r], provided that // r < len(replacementTable). If replacementTable[r] is the empty string then // no replacement is made. -// It also replaces the runes '\u2028' and '\u2029' with the strings -// `\u2028` and `\u2029`. Note the different quotes used. +// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and +// `\u2029`. func replace(s string, replacementTable []string) string { var b bytes.Buffer written := 0 @@ -237,6 +248,26 @@ var jsStrReplacementTable = []string{ '\\': `\\`, } +// jsStrNormReplacementTable is like jsStrReplacementTable but does not +// overencode existing escapes since this table has no entry for `\`. +var jsStrNormReplacementTable = []string{ + 0: `\0`, + '\t': `\t`, + '\n': `\n`, + '\v': `\x0b`, // "\v" == "v" on IE 6. + '\f': `\f`, + '\r': `\r`, + // Encode HTML specials as hex so the output can be embedded + // in HTML attributes without further encoding. + '"': `\x22`, + '&': `\x26`, + '\'': `\x27`, + '+': `\x2b`, + '/': `\/`, + '<': `\x3c`, + '>': `\x3e`, +} + var jsRegexpReplacementTable = []string{ 0: `\0`, '\t': `\t`, @@ -269,7 +300,7 @@ var jsRegexpReplacementTable = []string{ '}': `\}`, } -// isJSIdentPart is true if the given rune is a JS identifier part. +// isJSIdentPart returns whether the given rune is a JS identifier part. // It does not handle all the non-Latin letters, joiners, and combining marks, // but it does handle every codepoint that can occur in a numeric literal or // a keyword. diff --git a/src/pkg/exp/template/html/js_test.go b/src/pkg/exp/template/html/js_test.go index 0ae70d1e2..76fc23845 100644 --- a/src/pkg/exp/template/html/js_test.go +++ b/src/pkg/exp/template/html/js_test.go @@ -224,7 +224,7 @@ func TestJSRegexpEscaper(t *testing.T) { x interface{} esc string }{ - {"", ``}, + {"", `(?:)`}, {"foo", `foo`}, {"\u0000", `\0`}, {"\t", `\t`}, diff --git a/src/pkg/exp/template/html/transition.go b/src/pkg/exp/template/html/transition.go new file mode 100644 index 000000000..3be3a01a8 --- /dev/null +++ b/src/pkg/exp/template/html/transition.go @@ -0,0 +1,560 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "strings" +) + +// TODO: ensure transition error messages contain template name and ideally +// line info. + +// transitionFunc is the array of context transition functions for text nodes. +// A transition function takes a context and template text input, and returns +// the updated context and the number of bytes consumed from the front of the +// input. +var transitionFunc = [...]func(context, []byte) (context, int){ + stateText: tText, + stateTag: tTag, + stateAttrName: tAttrName, + stateAfterName: tAfterName, + stateBeforeValue: tBeforeValue, + stateHTMLCmt: tHTMLCmt, + stateRCDATA: tSpecialTagEnd, + stateAttr: tAttr, + stateURL: tURL, + stateJS: tJS, + stateJSDqStr: tJSDelimited, + stateJSSqStr: tJSDelimited, + stateJSRegexp: tJSDelimited, + stateJSBlockCmt: tBlockCmt, + stateJSLineCmt: tLineCmt, + stateCSS: tCSS, + stateCSSDqStr: tCSSStr, + stateCSSSqStr: tCSSStr, + stateCSSDqURL: tCSSStr, + stateCSSSqURL: tCSSStr, + stateCSSURL: tCSSStr, + stateCSSBlockCmt: tBlockCmt, + stateCSSLineCmt: tLineCmt, + stateError: tError, +} + +var commentStart = []byte("<!--") +var commentEnd = []byte("-->") + +// tText is the context transition function for the text state. +func tText(c context, s []byte) (context, int) { + k := 0 + for { + i := k + bytes.IndexByte(s[k:], '<') + if i < k || i+1 == len(s) { + return c, len(s) + } else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) { + return context{state: stateHTMLCmt}, i + 4 + } + i++ + end := false + if s[i] == '/' { + if i+1 == len(s) { + return c, len(s) + } + end, i = true, i+1 + } + j, e := eatTagName(s, i) + if j != i { + if end { + e = elementNone + } + // We've found an HTML tag. + return context{state: stateTag, element: e}, j + } + k = j + } + panic("unreachable") +} + +var elementContentType = [...]state{ + elementNone: stateText, + elementScript: stateJS, + elementStyle: stateCSS, + elementTextarea: stateRCDATA, + elementTitle: stateRCDATA, +} + +// tTag is the context transition function for the tag state. +func tTag(c context, s []byte) (context, int) { + // Find the attribute name. + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } + if s[i] == '>' { + return context{ + state: elementContentType[c.element], + element: c.element, + }, i + 1 + } + j, err := eatAttrName(s, i) + if err != nil { + return context{state: stateError, err: err}, len(s) + } + state, attr := stateTag, attrNone + if i != j { + canonAttrName := strings.ToLower(string(s[i:j])) + if urlAttr[canonAttrName] { + attr = attrURL + } else if strings.HasPrefix(canonAttrName, "on") { + attr = attrScript + } else if canonAttrName == "style" { + attr = attrStyle + } + if j == len(s) { + state = stateAttrName + } else { + state = stateAfterName + } + } + return context{state: state, element: c.element, attr: attr}, j +} + +// tAttrName is the context transition function for stateAttrName. +func tAttrName(c context, s []byte) (context, int) { + i, err := eatAttrName(s, 0) + if err != nil { + return context{state: stateError, err: err}, len(s) + } else if i != len(s) { + c.state = stateAfterName + } + return c, i +} + +// tAfterName is the context transition function for stateAfterName. +func tAfterName(c context, s []byte) (context, int) { + // Look for the start of the value. + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } else if s[i] != '=' { + // Occurs due to tag ending '>', and valueless attribute. + c.state = stateTag + return c, i + } + c.state = stateBeforeValue + // Consume the "=". + return c, i + 1 +} + +var attrStartStates = [...]state{ + attrNone: stateAttr, + attrScript: stateJS, + attrStyle: stateCSS, + attrURL: stateURL, +} + +// tBeforeValue is the context transition function for stateBeforeValue. +func tBeforeValue(c context, s []byte) (context, int) { + i := eatWhiteSpace(s, 0) + if i == len(s) { + return c, len(s) + } + // Find the attribute delimiter. + delim := delimSpaceOrTagEnd + switch s[i] { + case '\'': + delim, i = delimSingleQuote, i+1 + case '"': + delim, i = delimDoubleQuote, i+1 + } + c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone + return c, i +} + +// tHTMLCmt is the context transition function for stateHTMLCmt. +func tHTMLCmt(c context, s []byte) (context, int) { + if i := bytes.Index(s, commentEnd); i != -1 { + return context{}, i + 3 + } + return c, len(s) +} + +// specialTagEndMarkers maps element types to the character sequence that +// case-insensitively signals the end of the special tag body. +var specialTagEndMarkers = [...]string{ + elementScript: "</script", + elementStyle: "</style", + elementTextarea: "</textarea", + elementTitle: "</title", +} + +// tSpecialTagEnd is the context transition function for raw text and RCDATA +// element states. +func tSpecialTagEnd(c context, s []byte) (context, int) { + if c.element != elementNone { + if i := strings.Index(strings.ToLower(string(s)), specialTagEndMarkers[c.element]); i != -1 { + return context{}, i + } + } + return c, len(s) +} + +// tAttr is the context transition function for the attribute state. +func tAttr(c context, s []byte) (context, int) { + return c, len(s) +} + +// tURL is the context transition function for the URL state. +func tURL(c context, s []byte) (context, int) { + if bytes.IndexAny(s, "#?") >= 0 { + c.urlPart = urlPartQueryOrFrag + } else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone { + // HTML5 uses "Valid URL potentially surrounded by spaces" for + // attrs: http://www.w3.org/TR/html5/index.html#attributes-1 + c.urlPart = urlPartPreQuery + } + return c, len(s) +} + +// tJS is the context transition function for the JS state. +func tJS(c context, s []byte) (context, int) { + i := bytes.IndexAny(s, `"'/`) + if i == -1 { + // Entire input is non string, comment, regexp tokens. + c.jsCtx = nextJSCtx(s, c.jsCtx) + return c, len(s) + } + c.jsCtx = nextJSCtx(s[:i], c.jsCtx) + switch s[i] { + case '"': + c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp + case '\'': + c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp + case '/': + switch { + case i+1 < len(s) && s[i+1] == '/': + c.state, i = stateJSLineCmt, i+1 + case i+1 < len(s) && s[i+1] == '*': + c.state, i = stateJSBlockCmt, i+1 + case c.jsCtx == jsCtxRegexp: + c.state = stateJSRegexp + case c.jsCtx == jsCtxDivOp: + c.jsCtx = jsCtxRegexp + default: + return context{ + state: stateError, + err: errorf(ErrSlashAmbig, 0, "'/' could start div or regexp: %.32q", s[i:]), + }, len(s) + } + default: + panic("unreachable") + } + return c, i + 1 +} + +// tJSDelimited is the context transition function for the JS string and regexp +// states. +func tJSDelimited(c context, s []byte) (context, int) { + specials := `\"` + switch c.state { + case stateJSSqStr: + specials = `\'` + case stateJSRegexp: + specials = `\/[]` + } + + k, inCharset := 0, false + for { + i := k + bytes.IndexAny(s[k:], specials) + if i < k { + break + } + switch s[i] { + case '\\': + i++ + if i == len(s) { + return context{ + state: stateError, + err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s), + }, len(s) + } + case '[': + inCharset = true + case ']': + inCharset = false + default: + // end delimiter + if !inCharset { + c.state, c.jsCtx = stateJS, jsCtxDivOp + return c, i + 1 + } + } + k = i + 1 + } + + if inCharset { + // This can be fixed by making context richer if interpolation + // into charsets is desired. + return context{ + state: stateError, + err: errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s), + }, len(s) + } + + return c, len(s) +} + +var blockCommentEnd = []byte("*/") + +// tBlockCmt is the context transition function for /*comment*/ states. +func tBlockCmt(c context, s []byte) (context, int) { + i := bytes.Index(s, blockCommentEnd) + if i == -1 { + return c, len(s) + } + switch c.state { + case stateJSBlockCmt: + c.state = stateJS + case stateCSSBlockCmt: + c.state = stateCSS + default: + panic(c.state.String()) + } + return c, i + 2 +} + +// tLineCmt is the context transition function for //comment states. +func tLineCmt(c context, s []byte) (context, int) { + var lineTerminators string + var endState state + switch c.state { + case stateJSLineCmt: + lineTerminators, endState = "\n\r\u2028\u2029", stateJS + case stateCSSLineCmt: + lineTerminators, endState = "\n\f\r", stateCSS + // Line comments are not part of any published CSS standard but + // are supported by the 4 major browsers. + // This defines line comments as + // LINECOMMENT ::= "//" [^\n\f\d]* + // since http://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines + // newlines: + // nl ::= #xA | #xD #xA | #xD | #xC + default: + panic(c.state.String()) + } + + i := bytes.IndexAny(s, lineTerminators) + if i == -1 { + return c, len(s) + } + c.state = endState + // Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4 + // "However, the LineTerminator at the end of the line is not + // considered to be part of the single-line comment; it is + // recognized separately by the lexical grammar and becomes part + // of the stream of input elements for the syntactic grammar." + return c, i +} + +// tCSS is the context transition function for the CSS state. +func tCSS(c context, s []byte) (context, int) { + // CSS quoted strings are almost never used except for: + // (1) URLs as in background: "/foo.png" + // (2) Multiword font-names as in font-family: "Times New Roman" + // (3) List separators in content values as in inline-lists: + // <style> + // ul.inlineList { list-style: none; padding:0 } + // ul.inlineList > li { display: inline } + // ul.inlineList > li:before { content: ", " } + // ul.inlineList > li:first-child:before { content: "" } + // </style> + // <ul class=inlineList><li>One<li>Two<li>Three</ul> + // (4) Attribute value selectors as in a[href="http://example.com/"] + // + // We conservatively treat all strings as URLs, but make some + // allowances to avoid confusion. + // + // In (1), our conservative assumption is justified. + // In (2), valid font names do not contain ':', '?', or '#', so our + // conservative assumption is fine since we will never transition past + // urlPartPreQuery. + // In (3), our protocol heuristic should not be tripped, and there + // should not be non-space content after a '?' or '#', so as long as + // we only %-encode RFC 3986 reserved characters we are ok. + // In (4), we should URL escape for URL attributes, and for others we + // have the attribute name available if our conservative assumption + // proves problematic for real code. + + k := 0 + for { + i := k + bytes.IndexAny(s[k:], `("'/`) + if i < k { + return c, len(s) + } + switch s[i] { + case '(': + // Look for url to the left. + p := bytes.TrimRight(s[:i], "\t\n\f\r ") + if endsWithCSSKeyword(p, "url") { + j := len(s) - len(bytes.TrimLeft(s[i+1:], "\t\n\f\r ")) + switch { + case j != len(s) && s[j] == '"': + c.state, j = stateCSSDqURL, j+1 + case j != len(s) && s[j] == '\'': + c.state, j = stateCSSSqURL, j+1 + default: + c.state = stateCSSURL + } + return c, j + } + case '/': + if i+1 < len(s) { + switch s[i+1] { + case '/': + c.state = stateCSSLineCmt + return c, i + 2 + case '*': + c.state = stateCSSBlockCmt + return c, i + 2 + } + } + case '"': + c.state = stateCSSDqStr + return c, i + 1 + case '\'': + c.state = stateCSSSqStr + return c, i + 1 + } + k = i + 1 + } + panic("unreachable") +} + +// tCSSStr is the context transition function for the CSS string and URL states. +func tCSSStr(c context, s []byte) (context, int) { + var endAndEsc string + switch c.state { + case stateCSSDqStr, stateCSSDqURL: + endAndEsc = `\"` + case stateCSSSqStr, stateCSSSqURL: + endAndEsc = `\'` + case stateCSSURL: + // Unquoted URLs end with a newline or close parenthesis. + // The below includes the wc (whitespace character) and nl. + endAndEsc = "\\\t\n\f\r )" + default: + panic(c.state.String()) + } + + k := 0 + for { + i := k + bytes.IndexAny(s[k:], endAndEsc) + if i < k { + c, nread := tURL(c, decodeCSS(s[k:])) + return c, k + nread + } + if s[i] == '\\' { + i++ + if i == len(s) { + return context{ + state: stateError, + err: errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s), + }, len(s) + } + } else { + c.state = stateCSS + return c, i + 1 + } + c, _ = tURL(c, decodeCSS(s[:i+1])) + k = i + 1 + } + panic("unreachable") +} + +// tError is the context transition function for the error state. +func tError(c context, s []byte) (context, int) { + return c, len(s) +} + +// eatAttrName returns the largest j such that s[i:j] is an attribute name. +// It returns an error if s[i:] does not look like it begins with an +// attribute name, such as encountering a quote mark without a preceding +// equals sign. +func eatAttrName(s []byte, i int) (int, *Error) { + for j := i; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\n', '\f', '\r', '=', '>': + return j, nil + case '\'', '"', '<': + // These result in a parse warning in HTML5 and are + // indicative of serious problems if seen in an attr + // name in a template. + return -1, errorf(ErrBadHTML, 0, "%q in attribute name: %.32q", s[j:j+1], s) + default: + // No-op. + } + } + return len(s), nil +} + +var elementNameMap = map[string]element{ + "script": elementScript, + "style": elementStyle, + "textarea": elementTextarea, + "title": elementTitle, +} + +// eatTagName returns the largest j such that s[i:j] is a tag name and the tag type. +func eatTagName(s []byte, i int) (int, element) { + j := i + for ; j < len(s); j++ { + x := s[j] + if !(('a' <= x && x <= 'z') || + ('A' <= x && x <= 'Z') || + ('0' <= x && x <= '9' && i != j)) { + break + } + } + return j, elementNameMap[strings.ToLower(string(s[i:j]))] +} + +// eatWhiteSpace returns the largest j such that s[i:j] is white space. +func eatWhiteSpace(s []byte, i int) int { + for j := i; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\n', '\f', '\r': + // No-op. + default: + return j + } + } + return len(s) +} + +// urlAttr is the set of attribute names whose values are URLs. +// It consists of all "%URI"-typed attributes from +// http://www.w3.org/TR/html4/index/attributes.html +// as well as those attributes defined at +// http://dev.w3.org/html5/spec/index.html#attributes-1 +// whose Value column in that table matches +// "Valid [non-empty] URL potentially surrounded by spaces". +var urlAttr = map[string]bool{ + "action": true, + "archive": true, + "background": true, + "cite": true, + "classid": true, + "codebase": true, + "data": true, + "formaction": true, + "href": true, + "icon": true, + "longdesc": true, + "manifest": true, + "poster": true, + "profile": true, + "src": true, + "usemap": true, +} diff --git a/src/pkg/exp/template/html/url.go b/src/pkg/exp/template/html/url.go new file mode 100644 index 000000000..8a43e6364 --- /dev/null +++ b/src/pkg/exp/template/html/url.go @@ -0,0 +1,110 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "fmt" + "strings" +) + +// urlFilter returns the HTML equivalent of its input unless it contains an +// unsafe protocol in which case it defangs the entire URL. +func urlFilter(args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeURL { + return urlProcessor(true, s) + } + i := strings.IndexRune(s, ':') + if i >= 0 && strings.IndexRune(s[:i], '/') < 0 { + protocol := strings.ToLower(s[:i]) + if protocol != "http" && protocol != "https" && protocol != "mailto" { + // Return a value that someone investigating a bug + // report can put into a search engine. + return "#" + filterFailsafe + } + } + // TODO: Once we handle <style>#id { background: url({{.Img}}) }</style> + // we will need to stop this from HTML escaping and pipeline sanitizers. + return s +} + +// urlEscaper produces an output that can be embedded in a URL query. +// The output can be embedded in an HTML attribute without further escaping. +func urlEscaper(args ...interface{}) string { + return urlProcessor(false, args...) +} + +// urlEscaper normalizes URL content so it can be embedded in a quote-delimited +// string or parenthesis delimited url(...). +// The normalizer does not encode all HTML specials. Specifically, it does not +// encode '&' so correct embedding in an HTML attribute requires escaping of +// '&' to '&'. +func urlNormalizer(args ...interface{}) string { + return urlProcessor(true, args...) +} + +// urlProcessor normalizes (when norm is true) or escapes its input to produce +// a valid hierarchical or opaque URL part. +func urlProcessor(norm bool, args ...interface{}) string { + s, t := stringify(args...) + if t == contentTypeURL { + norm = true + } + var b bytes.Buffer + written := 0 + // The byte loop below assumes that all URLs use UTF-8 as the + // content-encoding. This is similar to the URI to IRI encoding scheme + // defined in section 3.1 of RFC 3987, and behaves the same as the + // EcmaScript builtin encodeURIComponent. + // It should not cause any misencoding of URLs in pages with + // Content-type: text/html;charset=UTF-8. + for i, n := 0, len(s); i < n; i++ { + c := s[i] + switch c { + // Single quote and parens are sub-delims in RFC 3986, but we + // escape them so the output can be embedded in in single + // quoted attributes and unquoted CSS url(...) constructs. + // Single quotes are reserved in URLs, but are only used in + // the obsolete "mark" rule in an appendix in RFC 3986 + // so can be safely encoded. + case '!', '#', '$', '&', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']': + if norm { + continue + } + // Unreserved according to RFC 3986 sec 2.3 + // "For consistency, percent-encoded octets in the ranges of + // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), + // period (%2E), underscore (%5F), or tilde (%7E) should not be + // created by URI producers + case '-', '.', '_', '~': + continue + case '%': + // When normalizing do not re-encode valid escapes. + if norm && i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) { + continue + } + default: + // Unreserved according to RFC 3986 sec 2.3 + if 'a' <= c && c <= 'z' { + continue + } + if 'A' <= c && c <= 'Z' { + continue + } + if '0' <= c && c <= '9' { + continue + } + } + b.WriteString(s[written:i]) + fmt.Fprintf(&b, "%%%02x", c) + written = i + 1 + } + if written == 0 { + return s + } + b.WriteString(s[written:]) + return b.String() +} diff --git a/src/pkg/exp/template/html/url_test.go b/src/pkg/exp/template/html/url_test.go new file mode 100644 index 000000000..b84623151 --- /dev/null +++ b/src/pkg/exp/template/html/url_test.go @@ -0,0 +1,112 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "testing" +) + +func TestURLNormalizer(t *testing.T) { + tests := []struct { + url, want string + }{ + {"", ""}, + { + "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag", + "http://example.com:80/foo/bar?q=foo%20&bar=x+y#frag", + }, + {" ", "%20"}, + {"%7c", "%7c"}, + {"%7C", "%7C"}, + {"%2", "%252"}, + {"%", "%25"}, + {"%z", "%25z"}, + {"/foo|bar/%5c\u1234", "/foo%7cbar/%5c%e1%88%b4"}, + } + for _, test := range tests { + if got := urlNormalizer(test.url); test.want != got { + t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.url, test.want, got) + } + if test.want != urlNormalizer(test.want) { + t.Errorf("not idempotent: %q", test.want) + } + } +} + +func TestURLFilters(t *testing.T) { + input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + + ` !"#$%&'()*+,-./` + + `0123456789:;<=>?` + + `@ABCDEFGHIJKLMNO` + + `PQRSTUVWXYZ[\]^_` + + "`abcdefghijklmno" + + "pqrstuvwxyz{|}~\x7f" + + "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") + + tests := []struct { + name string + escaper func(...interface{}) string + escaped string + }{ + { + "urlEscaper", + urlEscaper, + "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" + + "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" + + "%20%21%22%23%24%25%26%27%28%29%2a%2b%2c-.%2f" + + "0123456789%3a%3b%3c%3d%3e%3f" + + "%40ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ%5b%5c%5d%5e_" + + "%60abcdefghijklmno" + + "pqrstuvwxyz%7b%7c%7d~%7f" + + "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e", + }, + { + "urlNormalizer", + urlNormalizer, + "%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f" + + "%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f" + + "%20!%22#$%25&%27%28%29*+,-./" + + "0123456789:;%3c=%3e?" + + "@ABCDEFGHIJKLMNO" + + "PQRSTUVWXYZ[%5c]%5e_" + + "%60abcdefghijklmno" + + "pqrstuvwxyz%7b%7c%7d~%7f" + + "%c2%a0%c4%80%e2%80%a8%e2%80%a9%ef%bb%bf%f0%9d%84%9e", + }, + } + + for _, test := range tests { + if s := test.escaper(input); s != test.escaped { + t.Errorf("%s: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s) + continue + } + } +} + +func BenchmarkURLEscaper(b *testing.B) { + for i := 0; i < b.N; i++ { + urlEscaper("http://example.com:80/foo?q=bar%20&baz=x+y#frag") + } +} + +func BenchmarkURLEscaperNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + urlEscaper("TheQuickBrownFoxJumpsOverTheLazyDog.") + } +} + +func BenchmarkURLNormalizer(b *testing.B) { + for i := 0; i < b.N; i++ { + urlNormalizer("The quick brown fox jumps over the lazy dog.\n") + } +} + +func BenchmarkURLNormalizerNoSpecials(b *testing.B) { + for i := 0; i < b.N; i++ { + urlNormalizer("http://example.com:80/foo?q=bar%20&baz=x+y#frag") + } +} diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 38b65d1a1..f13f7a45c 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -286,6 +286,9 @@ func (f *FlagSet) Set(name, value string) bool { if !ok { return false } + if f.actual == nil { + f.actual = make(map[string]*Flag) + } f.actual[name] = flag return true } @@ -559,6 +562,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) { fmt.Fprintf(os.Stderr, "%s flag redefined: %s\n", f.name, name) panic("flag redefinition") // Happens only if flags are declared with identical names } + if f.formal == nil { + f.formal = make(map[string]*Flag) + } f.formal[name] = flag } @@ -586,6 +592,8 @@ func (f *FlagSet) failf(format string, a ...interface{}) os.Error { func (f *FlagSet) usage() { if f == commandLine { Usage() + } else if f.Usage == nil { + defaultUsage(f) } else { f.Usage() } @@ -657,6 +665,9 @@ func (f *FlagSet) parseOne() (bool, os.Error) { return false, f.failf("invalid value %q for flag: -%s", value, name) } } + if f.actual == nil { + f.actual = make(map[string]*Flag) + } f.actual[name] = flag return true, nil } @@ -713,10 +724,15 @@ var commandLine = NewFlagSet(os.Args[0], ExitOnError) func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, - actual: make(map[string]*Flag), - formal: make(map[string]*Flag), errorHandling: errorHandling, } - f.Usage = func() { defaultUsage(f) } return f } + +// Init sets the name and error handling property for a flag set. +// By default, the zero FlagSet uses an empty name and the +// ContinueOnError error handling policy. +func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { + f.name = name + f.errorHandling = errorHandling +} diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index 19c0deaf5..f13531669 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -180,7 +180,8 @@ func (f *flagVar) Set(value string) bool { } func TestUserDefined(t *testing.T) { - flags := NewFlagSet("test", ContinueOnError) + var flags FlagSet + flags.Init("test", ContinueOnError) var v flagVar flags.Var(&v, "v", "usage") if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go index 592ebbd9e..68a4180c9 100644 --- a/src/pkg/go/build/build_test.go +++ b/src/pkg/go/build/build_test.go @@ -27,7 +27,7 @@ var buildPkgs = []struct { &DirInfo{ GoFiles: []string{"pkgtest.go"}, SFiles: []string{"sqrt_" + runtime.GOARCH + ".s"}, - PkgName: "pkgtest", + Package: "pkgtest", Imports: []string{"os"}, TestImports: []string{"fmt", "pkgtest"}, TestGoFiles: sortstr([]string{"sqrt_test.go", "sqrt_" + runtime.GOARCH + "_test.go"}), @@ -38,7 +38,7 @@ var buildPkgs = []struct { "go/build/cmdtest", &DirInfo{ GoFiles: []string{"main.go"}, - PkgName: "main", + Package: "main", Imports: []string{"go/build/pkgtest"}, }, }, @@ -48,7 +48,7 @@ var buildPkgs = []struct { CgoFiles: []string{"cgotest.go"}, CFiles: []string{"cgotest.c"}, Imports: []string{"C", "unsafe"}, - PkgName: "cgotest", + Package: "cgotest", }, }, } @@ -59,8 +59,7 @@ func TestBuild(t *testing.T) { for _, tt := range buildPkgs { tree := Path[0] // Goroot dir := filepath.Join(tree.SrcDir(), tt.dir) - - info, err := ScanDir(dir, true) + info, err := ScanDir(dir) if err != nil { t.Errorf("ScanDir(%#q): %v", tt.dir, err) continue diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go index fa4d9e913..3ee10ab34 100644 --- a/src/pkg/go/build/dir.go +++ b/src/pkg/go/build/dir.go @@ -5,16 +5,22 @@ package build import ( + "bytes" + "fmt" + "go/ast" + "go/doc" "go/parser" "go/token" "io/ioutil" "log" "os" + "path" "path/filepath" + "runtime" "sort" "strconv" "strings" - "runtime" + "unicode" ) // A Context specifies the supporting context for a build. @@ -22,14 +28,55 @@ type Context struct { GOARCH string // target architecture GOOS string // target operating system // TODO(rsc,adg): GOPATH + + // By default, ScanDir uses the operating system's + // file system calls to read directories and files. + // Callers can override those calls to provide other + // ways to read data by setting ReadDir and ReadFile. + // ScanDir does not make any assumptions about the + // format of the strings dir and file: they can be + // slash-separated, backslash-separated, even URLs. + + // ReadDir returns a slice of *os.FileInfo, sorted by Name, + // describing the content of the named directory. + // The dir argument is the argument to ScanDir. + // If ReadDir is nil, ScanDir uses io.ReadDir. + ReadDir func(dir string) (fi []*os.FileInfo, err os.Error) + + // ReadFile returns the content of the file named file + // in the directory named dir. The dir argument is the + // argument to ScanDir, and the file argument is the + // Name field from an *os.FileInfo returned by ReadDir. + // The returned path is the full name of the file, to be + // used in error messages. + // + // If ReadFile is nil, ScanDir uses filepath.Join(dir, file) + // as the path and ioutil.ReadFile to read the data. + ReadFile func(dir, file string) (path string, content []byte, err os.Error) +} + +func (ctxt *Context) readDir(dir string) ([]*os.FileInfo, os.Error) { + if f := ctxt.ReadDir; f != nil { + return f(dir) + } + return ioutil.ReadDir(dir) +} + +func (ctxt *Context) readFile(dir, file string) (string, []byte, os.Error) { + if f := ctxt.ReadFile; f != nil { + return f(dir, file) + } + p := filepath.Join(dir, file) + content, err := ioutil.ReadFile(p) + return p, content, err } // The DefaultContext is the default Context for builds. // It uses the GOARCH and GOOS environment variables // if set, or else the compiled code's GOARCH and GOOS. var DefaultContext = Context{ - envOr("GOARCH", runtime.GOARCH), - envOr("GOOS", runtime.GOOS), + GOARCH: envOr("GOARCH", runtime.GOARCH), + GOOS: envOr("GOOS", runtime.GOOS), } func envOr(name, def string) string { @@ -41,36 +88,48 @@ func envOr(name, def string) string { } type DirInfo struct { - GoFiles []string // .go files in dir (excluding CgoFiles) - CgoFiles []string // .go files that import "C" - CFiles []string // .c files in dir - SFiles []string // .s files in dir - Imports []string // All packages imported by GoFiles - TestImports []string // All packages imported by (X)TestGoFiles - PkgName string // Name of package in dir + Package string // Name of package in dir + PackageComment *ast.CommentGroup // Package comments from GoFiles + ImportPath string // Import path of package in dir + Imports []string // All packages imported by GoFiles + + // Source files + GoFiles []string // .go files in dir (excluding CgoFiles) + CFiles []string // .c files in dir + SFiles []string // .s files in dir + CgoFiles []string // .go files that import "C" + + // Cgo directives + CgoPkgConfig []string // Cgo pkg-config directives + CgoCFLAGS []string // Cgo CFLAGS directives + CgoLDFLAGS []string // Cgo LDFLAGS directives + + // Test information TestGoFiles []string // _test.go files in package XTestGoFiles []string // _test.go files outside package + TestImports []string // All packages imported by (X)TestGoFiles } func (d *DirInfo) IsCommand() bool { - return d.PkgName == "main" + // TODO(rsc): This is at least a little bogus. + return d.Package == "main" } // ScanDir calls DefaultContext.ScanDir. -func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { - return DefaultContext.ScanDir(dir, allowMain) +func ScanDir(dir string) (info *DirInfo, err os.Error) { + return DefaultContext.ScanDir(dir) } // ScanDir returns a structure with details about the Go content found // in the given directory. The file lists exclude: // -// - files in package main (unless allowMain is true) +// - files in package main (unless no other package is found) // - files in package documentation // - files ending in _test.go -// - files starting with _ or . +// - files starting with _ or . // -func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { - dirs, err := ioutil.ReadDir(dir) +func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err os.Error) { + dirs, err := ctxt.readDir(dir) if err != nil { return nil, err } @@ -80,72 +139,118 @@ func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os. testImported := make(map[string]bool) fset := token.NewFileSet() for _, d := range dirs { + if !d.IsRegular() { + continue + } if strings.HasPrefix(d.Name, "_") || strings.HasPrefix(d.Name, ".") { continue } - if !ctxt.goodOSArch(d.Name) { + if !ctxt.goodOSArchFile(d.Name) { + continue + } + + ext := path.Ext(d.Name) + switch ext { + case ".go", ".c", ".s": + // tentatively okay + default: + // skip continue } - isTest := false - switch filepath.Ext(d.Name) { - case ".go": - isTest = strings.HasSuffix(d.Name, "_test.go") + // Look for +build comments to accept or reject the file. + filename, data, err := ctxt.readFile(dir, d.Name) + if err != nil { + return nil, err + } + if !ctxt.shouldBuild(data) { + continue + } + + // Going to save the file. For non-Go files, can stop here. + switch ext { case ".c": di.CFiles = append(di.CFiles, d.Name) continue case ".s": di.SFiles = append(di.SFiles, d.Name) continue - default: - continue } - filename := filepath.Join(dir, d.Name) - pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) + pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) if err != nil { return nil, err } + pkg := string(pf.Name.Name) - if pkg == "main" && !allowMain { + if pkg == "main" && di.Package != "" && di.Package != "main" { continue } if pkg == "documentation" { continue } + + isTest := strings.HasSuffix(d.Name, "_test.go") if isTest && strings.HasSuffix(pkg, "_test") { pkg = pkg[:len(pkg)-len("_test")] } - if di.PkgName == "" { - di.PkgName = pkg - } else if di.PkgName != pkg { - // Only if all files in the directory are in package main - // do we return PkgName=="main". - // A mix of main and another package reverts - // to the original (allowMain=false) behaviour. - if pkg == "main" || di.PkgName == "main" { - return ScanDir(dir, false) + + if pkg != di.Package && di.Package == "main" { + // Found non-main package but was recording + // information about package main. Reset. + di = DirInfo{} + } + if di.Package == "" { + di.Package = pkg + } else if pkg != di.Package { + return nil, fmt.Errorf("%s: found packages %s and %s", dir, pkg, di.Package) + } + if pf.Doc != nil { + if di.PackageComment != nil { + di.PackageComment.List = append(di.PackageComment.List, pf.Doc.List...) + } else { + di.PackageComment = pf.Doc } - return nil, os.NewError("multiple package names in " + dir) } + + // Record imports and information about cgo. isCgo := false - for _, spec := range pf.Imports { - quoted := string(spec.Path.Value) - path, err := strconv.Unquote(quoted) - if err != nil { - log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + for _, decl := range pf.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + continue } - if isTest { - testImported[path] = true - } else { - imported[path] = true - } - if path == "C" { + for _, dspec := range d.Specs { + spec, ok := dspec.(*ast.ImportSpec) + if !ok { + continue + } + quoted := string(spec.Path.Value) + path, err := strconv.Unquote(quoted) + if err != nil { + log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + } if isTest { - return nil, os.NewError("use of cgo in test " + filename) + testImported[path] = true + } else { + imported[path] = true + } + if path == "C" { + if isTest { + return nil, fmt.Errorf("%s: use of cgo in test not supported", filename) + } + cg := spec.Doc + if cg == nil && len(d.Specs) == 1 { + cg = d.Doc + } + if cg != nil { + if err := ctxt.saveCgo(filename, &di, cg); err != nil { + return nil, err + } + } + isCgo = true } - isCgo = true } } if isCgo { @@ -160,6 +265,9 @@ func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os. di.GoFiles = append(di.GoFiles, d.Name) } } + if di.Package == "" { + return nil, fmt.Errorf("%s: no Go source files", dir) + } di.Imports = make([]string, len(imported)) i := 0 for p := range imported { @@ -172,13 +280,245 @@ func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os. di.TestImports[i] = p i++ } - // File name lists are sorted because ioutil.ReadDir sorts. + // File name lists are sorted because ReadDir sorts. sort.Strings(di.Imports) sort.Strings(di.TestImports) return &di, nil } -// goodOSArch returns false if the name contains a $GOOS or $GOARCH +var slashslash = []byte("//") +var plusBuild = []byte("+build") + +// shouldBuild reports whether it is okay to use this file, +// The rule is that in the file's leading run of // comments +// and blank lines, which must be followed by a blank line +// (to avoid including a Go package clause doc comment), +// lines beginning with '// +build' are taken as build directives. +// +// The file is accepted only if each such line lists something +// matching the file. For example: +// +// // +build windows linux +// +// marks the file as applicable only on Windows and Linux. +// +func (ctxt *Context) shouldBuild(content []byte) bool { + // Pass 1. Identify leading run of // comments and blank lines, + // which must be followed by a blank line. + end := 0 + p := content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if len(line) == 0 { // Blank line + end = cap(content) - cap(line) // &line[0] - &content[0] + continue + } + if !bytes.HasPrefix(line, slashslash) { // Not comment line + break + } + } + content = content[:end] + + // Pass 2. Process each line in the run. + p = content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if bytes.HasPrefix(line, slashslash) { + line = bytes.TrimSpace(line[len(slashslash):]) + if len(line) > 0 && line[0] == '+' { + // Looks like a comment +line. + f := strings.Fields(string(line)) + if f[0] == "+build" { + ok := false + for _, tok := range f[1:] { + if ctxt.matchOSArch(tok) { + ok = true + break + } + } + if !ok { + return false // this one doesn't match + } + } + } + } + } + return true // everything matches +} + +// saveCgo saves the information from the #cgo lines in the import "C" comment. +// These lines set CFLAGS and LDFLAGS and pkg-config directives that affect +// the way cgo's C code is built. +// +// TODO(rsc): This duplicates code in cgo. +// Once the dust settles, remove this code from cgo. +func (ctxt *Context) saveCgo(filename string, di *DirInfo, cg *ast.CommentGroup) os.Error { + text := doc.CommentText(cg) + for _, line := range strings.Split(text, "\n") { + orig := line + + // Line is + // #cgo [GOOS/GOARCH...] LDFLAGS: stuff + // + line = strings.TrimSpace(line) + if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { + continue + } + + // Split at colon. + line = strings.TrimSpace(line[4:]) + i := strings.Index(line, ":") + if i < 0 { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + line, argstr := line[:i], line[i+1:] + + // Parse GOOS/GOARCH stuff. + f := strings.Fields(line) + if len(f) < 1 { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + + cond, verb := f[:len(f)-1], f[len(f)-1] + if len(cond) > 0 { + ok := false + for _, c := range cond { + if ctxt.matchOSArch(c) { + ok = true + break + } + } + if !ok { + continue + } + } + + args, err := splitQuoted(argstr) + if err != nil { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + for _, arg := range args { + if !safeName(arg) { + return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) + } + } + + switch verb { + case "CFLAGS": + di.CgoCFLAGS = append(di.CgoCFLAGS, args...) + case "LDFLAGS": + di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) + case "pkg-config": + di.CgoPkgConfig = append(di.CgoPkgConfig, args...) + default: + return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) + } + } + return nil +} + +var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") + +func safeName(s string) bool { + if s == "" { + return false + } + for i := 0; i < len(s); i++ { + if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { + return false + } + } + return true +} + +// splitQuoted splits the string s around each instance of one or more consecutive +// white space characters while taking into account quotes and escaping, and +// returns an array of substrings of s or an empty list if s contains only white space. +// Single quotes and double quotes are recognized to prevent splitting within the +// quoted region, and are removed from the resulting substrings. If a quote in s +// isn't closed err will be set and r will have the unclosed argument as the +// last element. The backslash is used for escaping. +// +// For example, the following string: +// +// a b:"c d" 'e''f' "g\"" +// +// Would be parsed as: +// +// []string{"a", "b:c d", "ef", `g"`} +// +func splitQuoted(s string) (r []string, err os.Error) { + var args []string + arg := make([]int, len(s)) + escaped := false + quoted := false + quote := 0 + i := 0 + for _, rune := range s { + switch { + case escaped: + escaped = false + case rune == '\\': + escaped = true + continue + case quote != 0: + if rune == quote { + quote = 0 + continue + } + case rune == '"' || rune == '\'': + quoted = true + quote = rune + continue + case unicode.IsSpace(rune): + if quoted || i > 0 { + quoted = false + args = append(args, string(arg[:i])) + i = 0 + } + continue + } + arg[i] = rune + i++ + } + if quoted || i > 0 { + args = append(args, string(arg[:i])) + } + if quote != 0 { + err = os.NewError("unclosed quote") + } else if escaped { + err = os.NewError("unfinished escaping") + } + return args, err +} + +// matchOSArch returns true if the name is one of: +// +// $GOOS +// $GOARCH +// $GOOS/$GOARCH +// +func (ctxt *Context) matchOSArch(name string) bool { + if name == ctxt.GOOS || name == ctxt.GOARCH { + return true + } + i := strings.Index(name, "/") + return i >= 0 && name[:i] == ctxt.GOOS && name[i+1:] == ctxt.GOARCH +} + +// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH // suffix which does not match the current system. // The recognized name formats are: // @@ -189,7 +529,7 @@ func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os. // name_$(GOARCH)_test.* // name_$(GOOS)_$(GOARCH)_test.* // -func (ctxt *Context) goodOSArch(name string) bool { +func (ctxt *Context) goodOSArchFile(name string) bool { if dot := strings.Index(name, "."); dot != -1 { name = name[:dot] } diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go index 2e8b4c865..d27630d75 100644 --- a/src/pkg/go/build/syslist_test.go +++ b/src/pkg/go/build/syslist_test.go @@ -55,8 +55,8 @@ var tests = []GoodFileTest{ func TestGoodOSArch(t *testing.T) { for _, test := range tests { - if DefaultContext.goodOSArch(test.name) != test.result { - t.Fatalf("goodOSArch(%q) != %v", test.name, test.result) + if DefaultContext.goodOSArchFile(test.name) != test.result { + t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) } } } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 871fefa0c..bfabd749a 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -13,7 +13,6 @@ import ( "io" "os" "path/filepath" - "runtime" "tabwriter" ) @@ -55,12 +54,17 @@ const ( noExtraLinebreak ) +// local error wrapper so we can distinguish os.Errors we want to return +// as errors from genuine panics (which we don't want to return as errors) +type osError struct { + err os.Error +} + type printer struct { // Configuration (does not change after initialization) output io.Writer Config - fset *token.FileSet - errors chan os.Error + fset *token.FileSet // Current state written int // number of bytes written @@ -95,7 +99,6 @@ func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS p.output = output p.Config = *cfg p.fset = fset - p.errors = make(chan os.Error) p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short p.nodeSizes = nodeSizes } @@ -143,8 +146,7 @@ func (p *printer) write0(data []byte) { n, err := p.output.Write(data) p.written += n if err != nil { - p.errors <- err - runtime.Goexit() + panic(osError{err}) } } } @@ -923,7 +925,7 @@ type Config struct { } // fprint implements Fprint and takes a nodesSizes map for setting up the printer state. -func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (int, os.Error) { +func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (written int, err os.Error) { // redirect output through a trimmer to eliminate trailing whitespace // (Input to a tabwriter must be untrimmed since trailing tabs provide // formatting information. The tabwriter could provide trimming @@ -950,47 +952,50 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{ output = tw } - // setup printer and print node + // setup printer var p printer p.init(output, cfg, fset, nodeSizes) - go func() { - switch n := node.(type) { - case ast.Expr: - p.useNodeComments = true - p.expr(n, ignoreMultiLine) - case ast.Stmt: - p.useNodeComments = true - // A labeled statement will un-indent to position the - // label. Set indent to 1 so we don't get indent "underflow". - if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { - p.indent = 1 - } - p.stmt(n, false, ignoreMultiLine) - case ast.Decl: - p.useNodeComments = true - p.decl(n, ignoreMultiLine) - case ast.Spec: - p.useNodeComments = true - p.spec(n, 1, false, ignoreMultiLine) - case *ast.File: - p.comments = n.Comments - p.useNodeComments = n.Comments == nil - p.file(n) - default: - p.errors <- fmt.Errorf("printer.Fprint: unsupported node type %T", n) - runtime.Goexit() + defer func() { + written = p.written + if e := recover(); e != nil { + err = e.(osError).err // re-panics if it's not a local osError } - p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) - p.errors <- nil // no errors }() - err := <-p.errors // wait for completion of goroutine + + // print node + switch n := node.(type) { + case ast.Expr: + p.useNodeComments = true + p.expr(n, ignoreMultiLine) + case ast.Stmt: + p.useNodeComments = true + // A labeled statement will un-indent to position the + // label. Set indent to 1 so we don't get indent "underflow". + if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { + p.indent = 1 + } + p.stmt(n, false, ignoreMultiLine) + case ast.Decl: + p.useNodeComments = true + p.decl(n, ignoreMultiLine) + case ast.Spec: + p.useNodeComments = true + p.spec(n, 1, false, ignoreMultiLine) + case *ast.File: + p.comments = n.Comments + p.useNodeComments = n.Comments == nil + p.file(n) + default: + panic(osError{fmt.Errorf("printer.Fprint: unsupported node type %T", n)}) + } + p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) // flush tabwriter, if any if tw != nil { tw.Flush() // ignore errors } - return p.written, err + return } // Fprint "pretty-prints" an AST node to output and returns the number diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index ff2d906b5..a644aa383 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -7,10 +7,10 @@ package printer import ( "bytes" "flag" - "io/ioutil" "go/ast" "go/parser" "go/token" + "io/ioutil" "path/filepath" "testing" "time" @@ -192,3 +192,15 @@ func TestLineComments(t *testing.T) { t.Errorf("got %d, expected %d\n", nlines, expected) } } + +// Verify that the printer can be invoked during initialization. +func init() { + const name = "foobar" + var buf bytes.Buffer + if err := Fprint(&buf, fset, &ast.Ident{Name: name}); err != nil { + panic(err) + } + if s := buf.String(); s != name { + panic("got " + s + ", want " + name) + } +} diff --git a/src/pkg/go/token/Makefile b/src/pkg/go/token/Makefile index 4a4e64dc8..b13b0442b 100644 --- a/src/pkg/go/token/Makefile +++ b/src/pkg/go/token/Makefile @@ -7,6 +7,7 @@ include ../../../Make.inc TARG=go/token GOFILES=\ position.go\ + serialize.go\ token.go\ include ../../../Make.pkg diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go index c559e19f8..9155b501d 100644 --- a/src/pkg/go/token/position.go +++ b/src/pkg/go/token/position.go @@ -136,10 +136,14 @@ func (s *FileSet) Position(p Pos) (pos Position) { return } +// A lineInfo object describes alternative file and line number +// information (such as provided via a //line comment in a .go +// file) for a given file offset. type lineInfo struct { - offset int - filename string - line int + // fields are exported to make them accessible to gob + Offset int + Filename string + Line int } // AddLineInfo adds alternative file and line number information for @@ -152,7 +156,7 @@ type lineInfo struct { // func (f *File) AddLineInfo(offset int, filename string, line int) { f.set.mutex.Lock() - if i := len(f.infos); i == 0 || f.infos[i-1].offset < offset && offset < f.size { + if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { f.infos = append(f.infos, lineInfo{offset, filename, line}) } f.set.mutex.Unlock() @@ -317,7 +321,7 @@ func searchInts(a []int, x int) int { } func searchLineInfos(a []lineInfo, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i].offset > x }) - 1 + return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1 } // info returns the file name, line, and column number for a file offset. @@ -330,9 +334,9 @@ func (f *File) info(offset int) (filename string, line, column int) { // almost no files have extra line infos if i := searchLineInfos(f.infos, offset); i >= 0 { alt := &f.infos[i] - filename = alt.filename - if i := searchInts(f.lines, alt.offset); i >= 0 { - line += alt.line - i - 1 + filename = alt.Filename + if i := searchInts(f.lines, alt.Offset); i >= 0 { + line += alt.Line - i - 1 } } } diff --git a/src/pkg/go/token/serialize.go b/src/pkg/go/token/serialize.go new file mode 100644 index 000000000..80a3323f9 --- /dev/null +++ b/src/pkg/go/token/serialize.go @@ -0,0 +1,62 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +import ( + "gob" + "io" + "os" +) + +type serializedFile struct { + // fields correspond 1:1 to fields with same (lower-case) name in File + Name string + Base int + Size int + Lines []int + Infos []lineInfo +} + +type serializedFileSet struct { + Base int + Files []serializedFile +} + +// Read reads the fileset from r into s; s must not be nil. +func (s *FileSet) Read(r io.Reader) os.Error { + var ss serializedFileSet + if err := gob.NewDecoder(r).Decode(&ss); err != nil { + return err + } + + s.mutex.Lock() + s.base = ss.Base + files := make([]*File, len(ss.Files)) + for i := 0; i < len(ss.Files); i++ { + f := &ss.Files[i] + files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos} + } + s.files = files + s.last = nil + s.mutex.Unlock() + + return nil +} + +// Write writes the fileset s to w. +func (s *FileSet) Write(w io.Writer) os.Error { + var ss serializedFileSet + + s.mutex.Lock() + ss.Base = s.base + files := make([]serializedFile, len(s.files)) + for i, f := range s.files { + files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos} + } + ss.Files = files + s.mutex.Unlock() + + return gob.NewEncoder(w).Encode(ss) +} diff --git a/src/pkg/go/token/serialize_test.go b/src/pkg/go/token/serialize_test.go new file mode 100644 index 000000000..24e419abf --- /dev/null +++ b/src/pkg/go/token/serialize_test.go @@ -0,0 +1,105 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +import ( + "bytes" + "fmt" + "os" + "testing" +) + +// equal returns nil if p and q describe the same file set; +// otherwise it returns an error describing the discrepancy. +func equal(p, q *FileSet) os.Error { + if p == q { + // avoid deadlock if p == q + return nil + } + + // not strictly needed for the test + p.mutex.Lock() + q.mutex.Lock() + defer q.mutex.Unlock() + defer p.mutex.Unlock() + + if p.base != q.base { + return fmt.Errorf("different bases: %d != %d", p.base, q.base) + } + + if len(p.files) != len(q.files) { + return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files)) + } + + for i, f := range p.files { + g := q.files[i] + if f.set != p { + return fmt.Errorf("wrong fileset for %q", f.name) + } + if g.set != q { + return fmt.Errorf("wrong fileset for %q", g.name) + } + if f.name != g.name { + return fmt.Errorf("different filenames: %q != %q", f.name, g.name) + } + if f.base != g.base { + return fmt.Errorf("different base for %q: %d != %d", f.name, f.base, g.base) + } + if f.size != g.size { + return fmt.Errorf("different size for %q: %d != %d", f.name, f.size, g.size) + } + for j, l := range f.lines { + m := g.lines[j] + if l != m { + return fmt.Errorf("different offsets for %q", f.name) + } + } + for j, l := range f.infos { + m := g.infos[j] + if l.Offset != m.Offset || l.Filename != m.Filename || l.Line != m.Line { + return fmt.Errorf("different infos for %q", f.name) + } + } + } + + // we don't care about .last - it's just a cache + return nil +} + +func checkSerialize(t *testing.T, p *FileSet) { + var buf bytes.Buffer + if err := p.Write(&buf); err != nil { + t.Errorf("writing fileset failed: %s", err) + return + } + q := NewFileSet() + if err := q.Read(&buf); err != nil { + t.Errorf("reading fileset failed: %s", err) + return + } + if err := equal(p, q); err != nil { + t.Errorf("filesets not identical: %s", err) + } +} + +func TestSerialization(t *testing.T) { + p := NewFileSet() + checkSerialize(t, p) + // add some files + for i := 0; i < 10; i++ { + f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100) + checkSerialize(t, p) + // add some lines and alternative file infos + line := 1000 + for offs := 0; offs < f.Size(); offs += 40 + i { + f.AddLine(offs) + if offs%7 == 0 { + f.AddLineInfo(offs, fmt.Sprintf("file%d", offs), line) + line += 33 + } + } + checkSerialize(t, p) + } +} diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go index a5fb91cda..2bcbf82a3 100644 --- a/src/pkg/gob/codec_test.go +++ b/src/pkg/gob/codec_test.go @@ -544,7 +544,7 @@ func TestScalarDecInstructions(t *testing.T) { var data struct { a []byte } - instr := &decInstr{decUint8Array, 6, 0, 0, ovfl} + instr := &decInstr{decUint8Slice, 6, 0, 0, ovfl} state := newDecodeStateFromData(bytesResult) execDec("bytes", instr, state, t, unsafe.Pointer(&data)) if string(data.a) != "hello" { diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go index bf7cb95f2..9d8d90587 100644 --- a/src/pkg/gob/decode.go +++ b/src/pkg/gob/decode.go @@ -70,13 +70,12 @@ func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err os.Erro if b <= 0x7f { return uint64(b), width, nil } - nb := -int(int8(b)) - if nb > uint64Size { + n := -int(int8(b)) + if n > uint64Size { err = errBadUint return } - var n int - n, err = io.ReadFull(r, buf[0:nb]) + width, err = io.ReadFull(r, buf[0:n]) if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF @@ -84,11 +83,10 @@ func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err os.Erro return } // Could check that the high byte is zero but it's not worth it. - for i := 0; i < n; i++ { - x <<= 8 - x |= uint64(buf[i]) - width++ + for _, b := range buf[0:width] { + x = x<<8 | uint64(b) } + width++ // +1 for length byte return } @@ -102,19 +100,18 @@ func (state *decoderState) decodeUint() (x uint64) { if b <= 0x7f { return uint64(b) } - nb := -int(int8(b)) - if nb > uint64Size { + n := -int(int8(b)) + if n > uint64Size { error(errBadUint) } - n, err := state.b.Read(state.buf[0:nb]) + width, err := state.b.Read(state.buf[0:n]) if err != nil { error(err) } // Don't need to check error; it's safe to loop regardless. // Could check that the high byte is zero but it's not worth it. - for i := 0; i < n; i++ { - x <<= 8 - x |= uint64(state.buf[i]) + for _, b := range state.buf[0:width] { + x = x<<8 | uint64(b) } return x } @@ -385,19 +382,29 @@ func decComplex128(i *decInstr, state *decoderState, p unsafe.Pointer) { *(*complex128)(p) = complex(real, imag) } -// decUint8Array decodes byte array and stores through p a slice header +// decUint8Slice decodes a byte slice and stores through p a slice header // describing the data. -// uint8 arrays are encoded as an unsigned count followed by the raw bytes. -func decUint8Array(i *decInstr, state *decoderState, p unsafe.Pointer) { +// uint8 slices are encoded as an unsigned count followed by the raw bytes. +func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) { if i.indir > 0 { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe.Pointer(new([]uint8)) } p = *(*unsafe.Pointer)(p) } - b := make([]uint8, state.decodeUint()) - state.b.Read(b) - *(*[]uint8)(p) = b + n := int(state.decodeUint()) + if n < 0 { + errorf("negative length decoding []byte") + } + slice := (*[]uint8)(p) + if cap(*slice) < n { + *slice = make([]uint8, n) + } else { + *slice = (*slice)[0:n] + } + if _, err := state.b.Read(*slice); err != nil { + errorf("error decoding []byte: %s", err) + } } // decString decodes byte array and stores through p a string header @@ -457,20 +464,17 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { // decodeSingle decodes a top-level value that is not a struct and stores it through p. // Such values are preceded by a zero, making them have the memory layout of a // struct field (although with an illegal field number). -func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr) (err os.Error) { - indir := ut.indir - if ut.isGobDecoder { - indir = int(ut.decIndir) - } - p = allocate(ut.base, p, indir) +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) (err os.Error) { state := dec.newDecoderState(&dec.buf) state.fieldnum = singletonField - basep := p delta := int(state.decodeUint()) if delta != 0 { errorf("decode: corrupted data: non-zero delta for singleton") } instr := &engine.instr[singletonField] + if instr.indir != ut.indir { + return os.NewError("gob: internal error: inconsistent indirection") + } ptr := unsafe.Pointer(basep) // offset will be zero if instr.indir > 1 { ptr = decIndirect(ptr, instr.indir) @@ -653,12 +657,15 @@ func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintpt } p = *(*uintptr)(up) } - // Allocate storage for the slice elements, that is, the underlying array. + // Allocate storage for the slice elements, that is, the underlying array, + // if the existing slice does not have the capacity. // Always write a header at p. hdrp := (*reflect.SliceHeader)(unsafe.Pointer(p)) - hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n)) + if hdrp.Cap < n { + hdrp.Data = uintptr(unsafe.NewArray(atyp.Elem(), n)) + hdrp.Cap = n + } hdrp.Len = n - hdrp.Cap = n dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) } @@ -842,7 +849,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg case reflect.Slice: name = "element of " + name if t.Elem().Kind() == reflect.Uint8 { - op = decUint8Array + op = decUint8Slice break } var elemId typeId @@ -1056,10 +1063,7 @@ func (dec *Decoder) typeString(remoteId typeId) string { // compileSingle compiles the decoder engine for a non-struct top-level value, including // GobDecoders. func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err os.Error) { - rt := ut.base - if ut.isGobDecoder { - rt = ut.user - } + rt := ut.user engine = new(decEngine) engine.instr = make([]decInstr, 1) // one item name := rt.String() // best we can do @@ -1189,7 +1193,7 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) { dec.decodeIgnoredValue(wireId) return } - // Dereference down to the underlying struct type. + // Dereference down to the underlying type. ut := userType(val.Type()) base := ut.base var enginePtr **decEngine diff --git a/src/pkg/gob/doc.go b/src/pkg/gob/doc.go index 35d882afb..05ebef195 100644 --- a/src/pkg/gob/doc.go +++ b/src/pkg/gob/doc.go @@ -68,7 +68,10 @@ the destination variable must be able to represent the value or the decode operation will fail. Structs, arrays and slices are also supported. Strings and arrays of bytes are -supported with a special, efficient representation (see below). +supported with a special, efficient representation (see below). When a slice is +decoded, if the existing slice has capacity the slice will be extended in place; +if not, a new array is allocated. Regardless, the length of the resuling slice +reports the number of elements decoded. Functions and channels cannot be sent in a gob. Attempting to encode a value that contains one will fail. @@ -221,6 +224,9 @@ In summary, a gob stream looks like where * signifies zero or more repetitions and the type id of a value must be predefined or be defined before the value in the stream. + +See "Gobs of data" for a design discussion of the gob wire format: +http://blog.golang.org/2011/03/gobs-of-data.html */ package gob diff --git a/src/pkg/gob/encode.go b/src/pkg/gob/encode.go index 317014efd..5100eaad5 100644 --- a/src/pkg/gob/encode.go +++ b/src/pkg/gob/encode.go @@ -59,15 +59,14 @@ func (state *encoderState) encodeUint(x uint64) { } return } - var n, m int - m = uint64Size - for n = 1; x > 0; n++ { - state.buf[m] = uint8(x) + i := uint64Size + for x > 0 { + state.buf[i] = uint8(x) x >>= 8 - m-- + i-- } - state.buf[m] = uint8(-(n - 1)) - n, err := state.b.Write(state.buf[m : uint64Size+1]) + state.buf[i] = uint8(i - uint64Size) // = loop count, negated + _, err := state.b.Write(state.buf[i : uint64Size+1]) if err != nil { error(err) } diff --git a/src/pkg/gob/encoder_test.go b/src/pkg/gob/encoder_test.go index f5ee423cb..79d289701 100644 --- a/src/pkg/gob/encoder_test.go +++ b/src/pkg/gob/encoder_test.go @@ -575,6 +575,56 @@ func TestGobMapInterfaceEncode(t *testing.T) { enc := NewEncoder(buf) err := enc.Encode(m) if err != nil { - t.Errorf("gob.Encode map: %s", err) + t.Errorf("encode map: %s", err) + } +} + +func TestSliceReusesMemory(t *testing.T) { + buf := bytes.NewBuffer(nil) + // Bytes + { + x := []byte("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("bytes: encode: %s", err) + } + // Decode into y, which is big enough. + y := []byte("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("bytes: decode:", err) + } + if !bytes.Equal(x, y) { + t.Errorf("bytes: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("bytes: unnecessary reallocation") + } + } + // general slice + { + x := []int("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("ints: encode: %s", err) + } + // Decode into y, which is big enough. + y := []int("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("ints: decode:", err) + } + if !reflect.DeepEqual(x, y) { + t.Errorf("ints: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("ints: unnecessary reallocation") + } } } diff --git a/src/pkg/gob/gobencdec_test.go b/src/pkg/gob/gobencdec_test.go index 371a43c8f..01addbe23 100644 --- a/src/pkg/gob/gobencdec_test.go +++ b/src/pkg/gob/gobencdec_test.go @@ -424,7 +424,7 @@ func TestGobEncoderNonStructSingleton(t *testing.T) { t.Fatal("decode error:", err) } if x != 1234 { - t.Errorf("expected 1234 got %c", x) + t.Errorf("expected 1234 got %d", x) } } @@ -488,3 +488,40 @@ func TestGobEncoderIgnoreNilEncoder(t *testing.T) { t.Errorf("expected x.G = nil, got %v", x.G) } } + +type gobDecoderBug0 struct { + foo, bar string +} + +func (br *gobDecoderBug0) String() string { + return br.foo + "-" + br.bar +} + +func (br *gobDecoderBug0) GobEncode() ([]byte, os.Error) { + return []byte(br.String()), nil +} + +func (br *gobDecoderBug0) GobDecode(b []byte) os.Error { + br.foo = "foo" + br.bar = "bar" + return nil +} + +// This was a bug: the receiver has a different indirection level +// than the variable. +func TestGobEncoderExtraIndirect(t *testing.T) { + gdb := &gobDecoderBug0{"foo", "bar"} + buf := new(bytes.Buffer) + e := NewEncoder(buf) + if err := e.Encode(gdb); err != nil { + t.Fatalf("encode: %v", err) + } + d := NewDecoder(buf) + var got *gobDecoderBug0 + if err := d.Decode(&got); err != nil { + t.Fatalf("decode: %v", err) + } + if got.foo != gdb.foo || got.bar != gdb.bar { + t.Errorf("got = %q, want %q", got, gdb) + } +} diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go index d36eaa19b..1d6382141 100644 --- a/src/pkg/http/cgi/host.go +++ b/src/pkg/http/cgi/host.go @@ -37,6 +37,7 @@ var osDefaultInheritEnv = map[string][]string{ "hpux": {"LD_LIBRARY_PATH", "SHLIB_PATH"}, "irix": {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"}, "linux": {"LD_LIBRARY_PATH"}, + "openbsd": {"LD_LIBRARY_PATH"}, "solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"}, "windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"}, } @@ -68,6 +69,31 @@ type Handler struct { PathLocationHandler http.Handler } +// removeLeadingDuplicates remove leading duplicate in environments. +// It's possible to override environment like following. +// cgi.Handler{ +// ... +// Env: []string{"SCRIPT_FILENAME=foo.php"}, +// } +func removeLeadingDuplicates(env []string) (ret []string) { + n := len(env) + for i := 0; i < n; i++ { + e := env[i] + s := strings.SplitN(e, "=", 2)[0] + found := false + for j := i + 1; j < n; j++ { + if s == strings.SplitN(env[j], "=", 2)[0] { + found = true + break + } + } + if !found { + ret = append(ret, e) + } + } + return +} + func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { root := h.Root if root == "" { @@ -149,6 +175,8 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } + env = removeLeadingDuplicates(env) + var cwd, path string if h.Dir != "" { path = h.Path diff --git a/src/pkg/http/cgi/host_test.go b/src/pkg/http/cgi/host_test.go index 1dc3abdbb..8111ba19e 100644 --- a/src/pkg/http/cgi/host_test.go +++ b/src/pkg/http/cgi/host_test.go @@ -447,3 +447,32 @@ func TestDirWindows(t *testing.T) { } runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) } + +func TestEnvOverride(t *testing.T) { + cgifile, _ := filepath.Abs("testdata/test.cgi") + + var perl string + var err os.Error + perl, err = exec.LookPath("perl") + if err != nil { + return + } + perl, _ = filepath.Abs(perl) + + cwd, _ := os.Getwd() + h := &Handler{ + Path: perl, + Root: "/test.cgi", + Dir: cwd, + Args: []string{cgifile}, + Env: []string{ + "SCRIPT_FILENAME=" + cgifile, + "REQUEST_URI=/foo/bar"}, + } + expectedMap := map[string]string{ + "cwd": cwd, + "env-SCRIPT_FILENAME": cgifile, + "env-REQUEST_URI": "/foo/bar", + } + runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) +} diff --git a/src/pkg/http/chunked.go b/src/pkg/http/chunked.go index 6c23e691f..eff9ae288 100644 --- a/src/pkg/http/chunked.go +++ b/src/pkg/http/chunked.go @@ -5,11 +5,11 @@ package http import ( + "bufio" "io" "log" "os" "strconv" - "bufio" ) // NewChunkedWriter returns a new writer that translates writes into HTTP diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 44b3443fc..8997a0792 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -76,7 +76,12 @@ type readClose struct { // Do sends an HTTP request and returns an HTTP response, following // policy (e.g. redirects, cookies, auth) as configured on the client. // -// Callers should close resp.Body when done reading from it. +// A non-nil response always contains a non-nil resp.Body. +// +// Callers should close resp.Body when done reading from it. If +// resp.Body is not closed, the Client's underlying RoundTripper +// (typically Transport) may not be able to re-use a persistent TCP +// connection to the server for a subsequent "keep-alive" request. // // Generally Get, Post, or PostForm will be used instead of Do. func (c *Client) Do(req *Request) (resp *Response, err os.Error) { diff --git a/src/pkg/http/cookie_test.go b/src/pkg/http/cookie_test.go index d7aeda0be..5de6aab61 100644 --- a/src/pkg/http/cookie_test.go +++ b/src/pkg/http/cookie_test.go @@ -124,7 +124,7 @@ var readSetCookiesTests = []struct { Path: "/", Domain: ".google.ch", HttpOnly: true, - Expires: time.Time{Year: 2011, Month: 11, Day: 23, Hour: 1, Minute: 5, Second: 3, Weekday: 3, ZoneOffset: 0, Zone: "GMT"}, + Expires: time.Time{Year: 2011, Month: 11, Day: 23, Hour: 1, Minute: 5, Second: 3, ZoneOffset: 0, Zone: "GMT"}, RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT", Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly", }}, diff --git a/src/pkg/http/dump.go b/src/pkg/http/dump.go index 358980f7c..f78df5771 100644 --- a/src/pkg/http/dump.go +++ b/src/pkg/http/dump.go @@ -44,7 +44,7 @@ func DumpRequest(req *Request, body bool) (dump []byte, err os.Error) { return } } - err = req.Write(&b) + err = req.dumpWrite(&b) req.Body = save if err != nil { return diff --git a/src/pkg/http/httptest/server.go b/src/pkg/http/httptest/server.go index 2ec36d04c..43a48ebbd 100644 --- a/src/pkg/http/httptest/server.go +++ b/src/pkg/http/httptest/server.go @@ -23,6 +23,10 @@ type Server struct { URL string // base URL of form http://ipaddr:port with no trailing slash Listener net.Listener TLS *tls.Config // nil if not using using TLS + + // Config may be changed after calling NewUnstartedServer and + // before Start or StartTLS. + Config *http.Server } // historyListener keeps track of all connections that it's ever @@ -41,6 +45,13 @@ func (hs *historyListener) Accept() (c net.Conn, err os.Error) { } func newLocalListener() net.Listener { + if *serve != "" { + l, err := net.Listen("tcp", *serve) + if err != nil { + panic(fmt.Sprintf("httptest: failed to listen on %v: %v", *serve, err)) + } + return l + } l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { @@ -59,51 +70,66 @@ var serve = flag.String("httptest.serve", "", "if non-empty, httptest.NewServer // NewServer starts and returns a new Server. // The caller should call Close when finished, to shut it down. func NewServer(handler http.Handler) *Server { - ts := new(Server) - var l net.Listener - if *serve != "" { - var err os.Error - l, err = net.Listen("tcp", *serve) - if err != nil { - panic(fmt.Sprintf("httptest: failed to listen on %v: %v", *serve, err)) - } - } else { - l = newLocalListener() + ts := NewUnstartedServer(handler) + ts.Start() + return ts +} + +// NewUnstartedServer returns a new Server but doesn't start it. +// +// After changing its configuration, the caller should call Start or +// StartTLS. +// +// The caller should call Close when finished, to shut it down. +func NewUnstartedServer(handler http.Handler) *Server { + return &Server{ + Listener: newLocalListener(), + Config: &http.Server{Handler: handler}, } - ts.Listener = &historyListener{l, make([]net.Conn, 0)} - ts.URL = "http://" + l.Addr().String() - server := &http.Server{Handler: handler} - go server.Serve(ts.Listener) +} + +// Start starts a server from NewUnstartedServer. +func (s *Server) Start() { + if s.URL != "" { + panic("Server already started") + } + s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)} + s.URL = "http://" + s.Listener.Addr().String() + go s.Config.Serve(s.Listener) if *serve != "" { - fmt.Println(os.Stderr, "httptest: serving on", ts.URL) + fmt.Println(os.Stderr, "httptest: serving on", s.URL) select {} } - return ts } -// NewTLSServer starts and returns a new Server using TLS. -// The caller should call Close when finished, to shut it down. -func NewTLSServer(handler http.Handler) *Server { - l := newLocalListener() - ts := new(Server) - +// StartTLS starts TLS on a server from NewUnstartedServer. +func (s *Server) StartTLS() { + if s.URL != "" { + panic("Server already started") + } cert, err := tls.X509KeyPair(localhostCert, localhostKey) if err != nil { panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) } - ts.TLS = &tls.Config{ + s.TLS = &tls.Config{ Rand: rand.Reader, Time: time.Seconds, NextProtos: []string{"http/1.1"}, Certificates: []tls.Certificate{cert}, } - tlsListener := tls.NewListener(l, ts.TLS) + tlsListener := tls.NewListener(s.Listener, s.TLS) + + s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} + s.URL = "https://" + s.Listener.Addr().String() + go s.Config.Serve(s.Listener) +} - ts.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} - ts.URL = "https://" + l.Addr().String() - server := &http.Server{Handler: handler} - go server.Serve(ts.Listener) +// NewTLSServer starts and returns a new Server using TLS. +// The caller should call Close when finished, to shut it down. +func NewTLSServer(handler http.Handler) *Server { + ts := NewUnstartedServer(handler) + ts.StartTLS() return ts } diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 610223139..dc344ca00 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -64,13 +64,20 @@ func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e // Headers that Request.Write handles itself and should be skipped. var reqWriteExcludeHeader = map[string]bool{ - "Host": true, + "Host": true, // not in Header map anyway "User-Agent": true, "Content-Length": true, "Transfer-Encoding": true, "Trailer": true, } +var reqWriteExcludeHeaderDump = map[string]bool{ + "Host": true, // not in Header map anyway + "Content-Length": true, + "Transfer-Encoding": true, + "Trailer": true, +} + // A Request represents a parsed HTTP request header. type Request struct { Method string // GET, POST, PUT, etc. @@ -273,14 +280,63 @@ func (req *Request) Write(w io.Writer) os.Error { } // WriteProxy is like Write but writes the request in the form -// expected by an HTTP proxy. It includes the scheme and host -// name in the URI instead of using a separate Host: header line. -// If req.RawURL is non-empty, WriteProxy uses it unchanged -// instead of URL but still omits the Host: header. +// expected by an HTTP proxy. In particular, WriteProxy writes the +// initial Request-URI line of the request with an absolute URI, per +// section 5.1.2 of RFC 2616, including the scheme and host. If +// req.RawURL is non-empty, WriteProxy uses it unchanged. In either +// case, WriteProxy also writes a Host header, using either req.Host +// or req.URL.Host. func (req *Request) WriteProxy(w io.Writer) os.Error { return req.write(w, true) } +func (req *Request) dumpWrite(w io.Writer) os.Error { + urlStr := req.RawURL + if urlStr == "" { + urlStr = valueOrDefault(req.URL.EncodedPath(), "/") + if req.URL.RawQuery != "" { + urlStr += "?" + req.URL.RawQuery + } + } + + bw := bufio.NewWriter(w) + fmt.Fprintf(bw, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), urlStr, + req.ProtoMajor, req.ProtoMinor) + + host := req.Host + if host == "" && req.URL != nil { + host = req.URL.Host + } + if host != "" { + fmt.Fprintf(bw, "Host: %s\r\n", host) + } + + // Process Body,ContentLength,Close,Trailer + tw, err := newTransferWriter(req) + if err != nil { + return err + } + err = tw.WriteHeader(bw) + if err != nil { + return err + } + + err = req.Header.WriteSubset(bw, reqWriteExcludeHeaderDump) + if err != nil { + return err + } + + io.WriteString(bw, "\r\n") + + // Write body and trailer + err = tw.WriteBody(bw) + if err != nil { + return err + } + bw.Flush() + return nil +} + func (req *Request) write(w io.Writer, usingProxy bool) os.Error { host := req.Host if host == "" { @@ -608,14 +664,14 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } -// MaxBytesReader is similar to io.LimitReader, but is intended for +// MaxBytesReader is similar to io.LimitReader but is intended for // limiting the size of incoming request bodies. In contrast to -// io.LimitReader, MaxBytesReader is a ReadCloser, returns a non-EOF -// error if the body is too large, and also takes care of closing the -// underlying io.ReadCloser connection (if applicable, usually a TCP -// connection) when the limit is hit. This prevents clients from -// accidentally or maliciously sending a large request and wasting -// server resources. +// io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a +// non-EOF error for a Read beyond the limit, and Closes the +// underlying reader when its Close method is called. +// +// MaxBytesReader prevents clients from accidentally or maliciously +// sending a large request and wasting server resources. func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { return &maxBytesReader{w: w, r: r, n: n} } @@ -673,7 +729,7 @@ func (r *Request) ParseForm() (err os.Error) { switch { case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "": var reader io.Reader = r.Body - maxFormSize := int64((1 << 63) - 1) + maxFormSize := int64(1<<63 - 1) if _, ok := r.Body.(*maxBytesReader); !ok { maxFormSize = int64(10 << 20) // 10 MB is a lot of text. reader = io.LimitReader(r.Body, maxFormSize+1) diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 458f0bd7f..8c29c44f4 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -16,16 +16,21 @@ import ( ) type reqWriteTest struct { - Req Request - Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body - Raw string - RawProxy string + Req Request + Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body + + // Any of these three may be empty to skip that test. + WantWrite string // Request.Write + WantProxy string // Request.WriteProxy + WantDump string // DumpRequest + + WantError os.Error // wanted error from Request.Write } var reqWriteTests = []reqWriteTest{ // HTTP/1.1 => chunked coding; no body; no trailer { - Request{ + Req: Request{ Method: "GET", RawURL: "http://www.techcrunch.com/", URL: &url.URL{ @@ -57,9 +62,7 @@ var reqWriteTests = []reqWriteTest{ Form: map[string][]string{}, }, - nil, - - "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + + WantWrite: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + "Host: www.techcrunch.com\r\n" + "User-Agent: Fake\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + @@ -69,7 +72,7 @@ var reqWriteTests = []reqWriteTest{ "Keep-Alive: 300\r\n" + "Proxy-Connection: keep-alive\r\n\r\n", - "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + + WantProxy: "GET http://www.techcrunch.com/ HTTP/1.1\r\n" + "Host: www.techcrunch.com\r\n" + "User-Agent: Fake\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + @@ -81,7 +84,7 @@ var reqWriteTests = []reqWriteTest{ }, // HTTP/1.1 => chunked coding; body; empty trailer { - Request{ + Req: Request{ Method: "GET", URL: &url.URL{ Scheme: "http", @@ -94,23 +97,28 @@ var reqWriteTests = []reqWriteTest{ TransferEncoding: []string{"chunked"}, }, - []byte("abcdef"), + Body: []byte("abcdef"), - "GET /search HTTP/1.1\r\n" + + WantWrite: "GET /search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + chunk("abcdef") + chunk(""), - "GET http://www.google.com/search HTTP/1.1\r\n" + + WantProxy: "GET http://www.google.com/search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + chunk("abcdef") + chunk(""), + + WantDump: "GET /search HTTP/1.1\r\n" + + "Host: www.google.com\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + chunk("abcdef") + chunk(""), }, // HTTP/1.1 POST => chunked coding; body; empty trailer { - Request{ + Req: Request{ Method: "POST", URL: &url.URL{ Scheme: "http", @@ -124,16 +132,16 @@ var reqWriteTests = []reqWriteTest{ TransferEncoding: []string{"chunked"}, }, - []byte("abcdef"), + Body: []byte("abcdef"), - "POST /search HTTP/1.1\r\n" + + WantWrite: "POST /search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + chunk("abcdef") + chunk(""), - "POST http://www.google.com/search HTTP/1.1\r\n" + + WantProxy: "POST http://www.google.com/search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Connection: close\r\n" + @@ -143,7 +151,7 @@ var reqWriteTests = []reqWriteTest{ // HTTP/1.1 POST with Content-Length, no chunking { - Request{ + Req: Request{ Method: "POST", URL: &url.URL{ Scheme: "http", @@ -157,9 +165,9 @@ var reqWriteTests = []reqWriteTest{ ContentLength: 6, }, - []byte("abcdef"), + Body: []byte("abcdef"), - "POST /search HTTP/1.1\r\n" + + WantWrite: "POST /search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Connection: close\r\n" + @@ -167,7 +175,7 @@ var reqWriteTests = []reqWriteTest{ "\r\n" + "abcdef", - "POST http://www.google.com/search HTTP/1.1\r\n" + + WantProxy: "POST http://www.google.com/search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Connection: close\r\n" + @@ -178,7 +186,7 @@ var reqWriteTests = []reqWriteTest{ // HTTP/1.1 POST with Content-Length in headers { - Request{ + Req: Request{ Method: "POST", RawURL: "http://example.com/", Host: "example.com", @@ -188,16 +196,16 @@ var reqWriteTests = []reqWriteTest{ ContentLength: 6, }, - []byte("abcdef"), + Body: []byte("abcdef"), - "POST http://example.com/ HTTP/1.1\r\n" + + WantWrite: "POST http://example.com/ HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + "Content-Length: 6\r\n" + "\r\n" + "abcdef", - "POST http://example.com/ HTTP/1.1\r\n" + + WantProxy: "POST http://example.com/ HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + "Content-Length: 6\r\n" + @@ -207,21 +215,19 @@ var reqWriteTests = []reqWriteTest{ // default to HTTP/1.1 { - Request{ + Req: Request{ Method: "GET", RawURL: "/search", Host: "www.google.com", }, - nil, - - "GET /search HTTP/1.1\r\n" + + WantWrite: "GET /search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "\r\n", // Looks weird but RawURL overrides what WriteProxy would choose. - "GET /search HTTP/1.1\r\n" + + WantProxy: "GET /search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "\r\n", @@ -229,7 +235,7 @@ var reqWriteTests = []reqWriteTest{ // Request with a 0 ContentLength and a 0 byte body. { - Request{ + Req: Request{ Method: "POST", RawURL: "/", Host: "example.com", @@ -238,22 +244,27 @@ var reqWriteTests = []reqWriteTest{ ContentLength: 0, // as if unset by user }, - func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) }, + Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) }, - "POST / HTTP/1.1\r\n" + + // RFC 2616 Section 14.13 says Content-Length should be specified + // unless body is prohibited by the request method. + // Also, nginx expects it for POST and PUT. + WantWrite: "POST / HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + + "Content-Length: 0\r\n" + "\r\n", - "POST / HTTP/1.1\r\n" + + WantProxy: "POST / HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + + "Content-Length: 0\r\n" + "\r\n", }, // Request with a 0 ContentLength and a 1 byte body. { - Request{ + Req: Request{ Method: "POST", RawURL: "/", Host: "example.com", @@ -262,20 +273,84 @@ var reqWriteTests = []reqWriteTest{ ContentLength: 0, // as if unset by user }, - func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) }, + Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) }, - "POST / HTTP/1.1\r\n" + + WantWrite: "POST / HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + chunk("x") + chunk(""), - "POST / HTTP/1.1\r\n" + + WantProxy: "POST / HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + chunk("x") + chunk(""), }, + + // Request with a ContentLength of 10 but a 5 byte body. + { + Req: Request{ + Method: "POST", + RawURL: "/", + Host: "example.com", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 10, // but we're going to send only 5 bytes + }, + Body: []byte("12345"), + WantError: os.NewError("http: Request.ContentLength=10 with Body length 5"), + }, + + // Request with a ContentLength of 4 but an 8 byte body. + { + Req: Request{ + Method: "POST", + RawURL: "/", + Host: "example.com", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 4, // but we're going to try to send 8 bytes + }, + Body: []byte("12345678"), + WantError: os.NewError("http: Request.ContentLength=4 with Body length 8"), + }, + + // Request with a 5 ContentLength and nil body. + { + Req: Request{ + Method: "POST", + RawURL: "/", + Host: "example.com", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 5, // but we'll omit the body + }, + WantError: os.NewError("http: Request.ContentLength=5 with nil Body"), + }, + + // Verify that DumpRequest preserves the HTTP version number, doesn't add a Host, + // and doesn't add a User-Agent. + { + Req: Request{ + Method: "GET", + RawURL: "/foo", + ProtoMajor: 1, + ProtoMinor: 0, + Header: Header{ + "X-Foo": []string{"X-Bar"}, + }, + }, + + // We can dump it: + WantDump: "GET /foo HTTP/1.0\r\n" + + "X-Foo: X-Bar\r\n\r\n", + + // .. but we can't call Request.Write on it, due to its lack of Host header. + // TODO(bradfitz): there might be an argument to allow this, but for now I'd + // rather let HTTP/1.0 continue to die. + WantError: os.NewError("http: Request.Write on Request with no Host or URL set"), + }, } func TestRequestWrite(t *testing.T) { @@ -283,6 +358,9 @@ func TestRequestWrite(t *testing.T) { tt := &reqWriteTests[i] setBody := func() { + if tt.Body == nil { + return + } switch b := tt.Body.(type) { case []byte: tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b)) @@ -290,37 +368,55 @@ func TestRequestWrite(t *testing.T) { tt.Req.Body = b() } } - if tt.Body != nil { - setBody() - } + setBody() if tt.Req.Header == nil { tt.Req.Header = make(Header) } + var braw bytes.Buffer err := tt.Req.Write(&braw) - if err != nil { - t.Errorf("error writing #%d: %s", i, err) + if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.WantError); g != e { + t.Errorf("writing #%d, err = %q, want %q", i, g, e) continue } - sraw := braw.String() - if sraw != tt.Raw { - t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, sraw) + if err != nil { continue } - if tt.Body != nil { - setBody() + if tt.WantWrite != "" { + sraw := braw.String() + if sraw != tt.WantWrite { + t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantWrite, sraw) + continue + } } - var praw bytes.Buffer - err = tt.Req.WriteProxy(&praw) - if err != nil { - t.Errorf("error writing #%d: %s", i, err) - continue + + if tt.WantProxy != "" { + setBody() + var praw bytes.Buffer + err = tt.Req.WriteProxy(&praw) + if err != nil { + t.Errorf("WriteProxy #%d: %s", i, err) + continue + } + sraw := praw.String() + if sraw != tt.WantProxy { + t.Errorf("Test Proxy %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantProxy, sraw) + continue + } } - sraw = praw.String() - if sraw != tt.RawProxy { - t.Errorf("Test Proxy %d, expecting:\n%s\nGot:\n%s\n", i, tt.RawProxy, sraw) - continue + + if tt.WantDump != "" { + setBody() + dump, err := DumpRequest(&tt.Req, true) + if err != nil { + t.Errorf("DumpRequest #%d: %s", i, err) + continue + } + if string(dump) != tt.WantDump { + t.Errorf("DumpRequest %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantDump, string(dump)) + continue + } } } } diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go index 915327a69..b01a303a1 100644 --- a/src/pkg/http/response.go +++ b/src/pkg/http/response.go @@ -41,6 +41,10 @@ type Response struct { Header Header // Body represents the response body. + // + // The http Client and Transport guarantee that Body is always + // non-nil, even on responses without a body or responses with + // a zero-lengthed body. Body io.ReadCloser // ContentLength records the length of the associated content. The diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index 17439110f..1bb748c3c 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -535,6 +535,30 @@ func TestHeadResponses(t *testing.T) { } } +func TestTLSHandshakeTimeout(t *testing.T) { + if true { + t.Logf("Skipping broken test; issue 2281") + return + } + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) + ts.Config.ReadTimeout = 250e6 + ts.StartTLS() + defer ts.Close() + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer conn.Close() + timer := time.AfterFunc(10e9, func() { t.Fatalf("Timeout") }) + defer timer.Stop() + + var buf [1]byte + n, err := conn.Read(buf[:]) + if err == nil || n != 0 { + t.Errorf("Read = %d, %v; want an error and no bytes", n, err) + } +} + func TestTLSServer(t *testing.T) { ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.TLS != nil { @@ -545,6 +569,19 @@ func TestTLSServer(t *testing.T) { } })) defer ts.Close() + + // Connect an idle TCP connection to this server before we run + // our real tests. This idle connection used to block forever + // in the TLS handshake, preventing future connections from + // being accepted. It may prevent future accidental blocking + // in newConn. + idleConn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer idleConn.Close() + time.AfterFunc(10e9, func() { t.Fatalf("Timeout") }) + if !strings.HasPrefix(ts.URL, "https://") { t.Fatalf("expected test TLS server to start with https://, got %q", ts.URL) } diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 654af378a..6be3611f0 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -178,13 +178,6 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err os.Error) { br := bufio.NewReader(c.lr) bw := bufio.NewWriter(rwc) c.buf = bufio.NewReadWriter(br, bw) - - if tlsConn, ok := rwc.(*tls.Conn); ok { - tlsConn.Handshake() - c.tlsState = new(tls.ConnectionState) - *c.tlsState = tlsConn.ConnectionState() - } - return c, nil } @@ -562,6 +555,12 @@ func (c *conn) serve() { log.Print(buf.String()) }() + if tlsConn, ok := c.rwc.(*tls.Conn); ok { + tlsConn.Handshake() + c.tlsState = new(tls.ConnectionState) + *c.tlsState = tlsConn.ConnectionState() + } + for { w, err := c.readRequest() if err != nil { diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index 0a754d20a..300c7a88d 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -7,6 +7,7 @@ package http import ( "bytes" "bufio" + "fmt" "io" "io/ioutil" "os" @@ -18,10 +19,11 @@ import ( // sanitizes them without changing the user object and provides methods for // writing the respective header, body and trailer in wire format. type transferWriter struct { + Method string Body io.Reader BodyCloser io.Closer ResponseToHEAD bool - ContentLength int64 + ContentLength int64 // -1 means unknown, 0 means exactly none Close bool TransferEncoding []string Trailer Header @@ -34,6 +36,10 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) { atLeastHTTP11 := false switch rr := r.(type) { case *Request: + if rr.ContentLength != 0 && rr.Body == nil { + return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength) + } + t.Method = rr.Method t.Body = rr.Body t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength @@ -64,6 +70,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) { } } case *Response: + t.Method = rr.Request.Method t.Body = rr.Body t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength @@ -105,6 +112,27 @@ func noBodyExpected(requestMethod string) bool { return requestMethod == "HEAD" } +func (t *transferWriter) shouldSendContentLength() bool { + if chunked(t.TransferEncoding) { + return false + } + if t.ContentLength > 0 { + return true + } + if t.ResponseToHEAD { + return true + } + // Many servers expect a Content-Length for these methods + if t.Method == "POST" || t.Method == "PUT" { + return true + } + if t.ContentLength == 0 && isIdentity(t.TransferEncoding) { + return true + } + + return false +} + func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { if t.Close { _, err = io.WriteString(w, "Connection: close\r\n") @@ -116,14 +144,14 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { // Write Content-Length and/or Transfer-Encoding whose values are a // function of the sanitized field triple (Body, ContentLength, // TransferEncoding) - if chunked(t.TransferEncoding) { - _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n") + if t.shouldSendContentLength() { + io.WriteString(w, "Content-Length: ") + _, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n") if err != nil { return } - } else if t.ContentLength > 0 || t.ResponseToHEAD || (t.ContentLength == 0 && isIdentity(t.TransferEncoding)) { - io.WriteString(w, "Content-Length: ") - _, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n") + } else if chunked(t.TransferEncoding) { + _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n") if err != nil { return } @@ -154,6 +182,8 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { } func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) { + var ncopy int64 + // Write body if t.Body != nil { if chunked(t.TransferEncoding) { @@ -163,9 +193,14 @@ func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) { err = cw.Close() } } else if t.ContentLength == -1 { - _, err = io.Copy(w, t.Body) + ncopy, err = io.Copy(w, t.Body) } else { - _, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength)) + ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength)) + nextra, err := io.Copy(ioutil.Discard, t.Body) + if err != nil { + return err + } + ncopy += nextra } if err != nil { return err @@ -175,6 +210,11 @@ func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) { } } + if t.ContentLength != -1 && t.ContentLength != ncopy { + return fmt.Errorf("http: Request.ContentLength=%d with Body length %d", + t.ContentLength, ncopy) + } + // TODO(petar): Place trailer writer code here. if chunked(t.TransferEncoding) { // Last chunk, empty trailer diff --git a/src/pkg/image/bmp/reader.go b/src/pkg/image/bmp/reader.go index 357da1dac..6bf4b1dbb 100644 --- a/src/pkg/image/bmp/reader.go +++ b/src/pkg/image/bmp/reader.go @@ -28,7 +28,7 @@ func readUint32(b []byte) uint32 { // decodePaletted reads an 8 bit-per-pixel BMP image from r. func decodePaletted(r io.Reader, c image.Config) (image.Image, os.Error) { var tmp [4]byte - paletted := image.NewPaletted(c.Width, c.Height, c.ColorModel.(image.PalettedColorModel)) + paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(image.PalettedColorModel)) // BMP images are stored bottom-up rather than top-down. for y := c.Height - 1; y >= 0; y-- { p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width] @@ -49,7 +49,7 @@ func decodePaletted(r io.Reader, c image.Config) (image.Image, os.Error) { // decodeRGBA reads a 24 bit-per-pixel BMP image from r. func decodeRGBA(r io.Reader, c image.Config) (image.Image, os.Error) { - rgba := image.NewRGBA(c.Width, c.Height) + rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height)) // There are 3 bytes per pixel, and each row is 4-byte aligned. b := make([]byte, (3*c.Width+3)&^3) // BMP images are stored bottom-up rather than top-down. diff --git a/src/pkg/image/draw/bench_test.go b/src/pkg/image/draw/bench_test.go index a99b40814..0e5e324dc 100644 --- a/src/pkg/image/draw/bench_test.go +++ b/src/pkg/image/draw/bench_test.go @@ -24,7 +24,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { var dst Image switch dcm { case image.RGBAColorModel: - dst1 := image.NewRGBA(dstw, dsth) + dst1 := image.NewRGBA(image.Rect(0, 0, dstw, dsth)) for y := 0; y < dsth; y++ { for x := 0; x < dstw; x++ { dst1.SetRGBA(x, y, image.RGBAColor{ @@ -37,7 +37,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { } dst = dst1 case image.RGBA64ColorModel: - dst1 := image.NewRGBA64(dstw, dsth) + dst1 := image.NewRGBA64(image.Rect(0, 0, dstw, dsth)) for y := 0; y < dsth; y++ { for x := 0; x < dstw; x++ { dst1.SetRGBA64(x, y, image.RGBA64Color{ @@ -58,7 +58,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { case nil: src = &image.ColorImage{image.RGBAColor{0x11, 0x22, 0x33, 0xff}} case image.RGBAColorModel: - src1 := image.NewRGBA(srcw, srch) + src1 := image.NewRGBA(image.Rect(0, 0, srcw, srch)) for y := 0; y < srch; y++ { for x := 0; x < srcw; x++ { src1.SetRGBA(x, y, image.RGBAColor{ @@ -71,7 +71,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { } src = src1 case image.RGBA64ColorModel: - src1 := image.NewRGBA64(srcw, srch) + src1 := image.NewRGBA64(image.Rect(0, 0, srcw, srch)) for y := 0; y < srch; y++ { for x := 0; x < srcw; x++ { src1.SetRGBA64(x, y, image.RGBA64Color{ @@ -84,7 +84,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { } src = src1 case image.NRGBAColorModel: - src1 := image.NewNRGBA(srcw, srch) + src1 := image.NewNRGBA(image.Rect(0, 0, srcw, srch)) for y := 0; y < srch; y++ { for x := 0; x < srcw; x++ { src1.SetNRGBA(x, y, image.NRGBAColor{ @@ -123,7 +123,7 @@ func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) { case nil: // No-op. case image.AlphaColorModel: - mask1 := image.NewAlpha(srcw, srch) + mask1 := image.NewAlpha(image.Rect(0, 0, srcw, srch)) for y := 0; y < srch; y++ { for x := 0; x < srcw; x++ { a := uint8((23*x + 29*y) % 0x100) diff --git a/src/pkg/image/draw/clip_test.go b/src/pkg/image/draw/clip_test.go index db40d82f5..65381f72f 100644 --- a/src/pkg/image/draw/clip_test.go +++ b/src/pkg/image/draw/clip_test.go @@ -143,9 +143,9 @@ var clipTests = []clipTest{ } func TestClip(t *testing.T) { - dst0 := image.NewRGBA(100, 100) - src0 := image.NewRGBA(100, 100) - mask0 := image.NewRGBA(100, 100) + dst0 := image.NewRGBA(image.Rect(0, 0, 100, 100)) + src0 := image.NewRGBA(image.Rect(0, 0, 100, 100)) + mask0 := image.NewRGBA(image.Rect(0, 0, 100, 100)) for _, c := range clipTests { dst := dst0.SubImage(c.dr).(*image.RGBA) src := src0.SubImage(c.sr).(*image.RGBA) diff --git a/src/pkg/image/draw/draw_test.go b/src/pkg/image/draw/draw_test.go index 55435cc27..7634c2e8b 100644 --- a/src/pkg/image/draw/draw_test.go +++ b/src/pkg/image/draw/draw_test.go @@ -25,7 +25,7 @@ func fillAlpha(alpha int) image.Image { } func vgradGreen(alpha int) image.Image { - m := image.NewRGBA(16, 16) + m := image.NewRGBA(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { for x := 0; x < 16; x++ { m.Set(x, y, image.RGBAColor{0, uint8(y * alpha / 15), 0, uint8(alpha)}) @@ -35,7 +35,7 @@ func vgradGreen(alpha int) image.Image { } func vgradAlpha(alpha int) image.Image { - m := image.NewAlpha(16, 16) + m := image.NewAlpha(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { for x := 0; x < 16; x++ { m.Set(x, y, image.AlphaColor{uint8(y * alpha / 15)}) @@ -45,7 +45,7 @@ func vgradAlpha(alpha int) image.Image { } func vgradGreenNRGBA(alpha int) image.Image { - m := image.NewNRGBA(16, 16) + m := image.NewNRGBA(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { for x := 0; x < 16; x++ { m.Set(x, y, image.RGBAColor{0, uint8(y * 0x11), 0, uint8(alpha)}) @@ -73,7 +73,7 @@ func vgradCr() image.Image { } func hgradRed(alpha int) Image { - m := image.NewRGBA(16, 16) + m := image.NewRGBA(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { for x := 0; x < 16; x++ { m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), 0, 0, uint8(alpha)}) @@ -83,7 +83,7 @@ func hgradRed(alpha int) Image { } func gradYellow(alpha int) Image { - m := image.NewRGBA(16, 16) + m := image.NewRGBA(image.Rect(0, 0, 16, 16)) for y := 0; y < 16; y++ { for x := 0; x < 16; x++ { m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)}) @@ -163,7 +163,7 @@ func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Po if mask != nil { mb = mask.Bounds() } - golden := image.NewRGBA(b.Max.X, b.Max.Y) + golden := image.NewRGBA(image.Rect(0, 0, b.Max.X, b.Max.Y)) for y := r.Min.Y; y < r.Max.Y; y++ { sy := y + sp.Y - r.Min.Y my := y + mp.Y - r.Min.Y @@ -281,8 +281,8 @@ func TestDrawOverlap(t *testing.T) { // TestNonZeroSrcPt checks drawing with a non-zero src point parameter. func TestNonZeroSrcPt(t *testing.T) { - a := image.NewRGBA(1, 1) - b := image.NewRGBA(2, 2) + a := image.NewRGBA(image.Rect(0, 0, 1, 1)) + b := image.NewRGBA(image.Rect(0, 0, 2, 2)) b.Set(0, 0, image.RGBAColor{0, 0, 0, 5}) b.Set(1, 0, image.RGBAColor{0, 0, 5, 5}) b.Set(0, 1, image.RGBAColor{0, 5, 0, 5}) @@ -310,7 +310,7 @@ func TestFill(t *testing.T) { image.Rect(20, 20, 29, 29), } for _, r := range rr { - m := image.NewRGBA(40, 30).SubImage(r).(*image.RGBA) + m := image.NewRGBA(image.Rect(0, 0, 40, 30)).SubImage(r).(*image.RGBA) b := m.Bounds() c := image.RGBAColor{11, 0, 0, 255} src := &image.ColorImage{c} diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go index e39b79746..48876f3a6 100644 --- a/src/pkg/image/gif/reader.go +++ b/src/pkg/image/gif/reader.go @@ -334,10 +334,7 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, os.Error) { width := int(d.tmp[4]) + int(d.tmp[5])<<8 height := int(d.tmp[6]) + int(d.tmp[7])<<8 d.imageFields = d.tmp[8] - m := image.NewPaletted(width, height, nil) - // Overwrite the rectangle to take account of left and top. - m.Rect = image.Rect(left, top, left+width, top+height) - return m, nil + return image.NewPaletted(image.Rect(left, top, left+width, top+height), nil), nil } func (d *decoder) readBlock() (int, os.Error) { diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index a01cda864..1ff0c023a 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -118,9 +118,10 @@ func (p *RGBA) Opaque() bool { } // NewRGBA returns a new RGBA with the given width and height. -func NewRGBA(w, h int) *RGBA { +func NewRGBA(r Rectangle) *RGBA { + w, h := r.Dx(), r.Dy() buf := make([]uint8, 4*w*h) - return &RGBA{buf, 4 * w, Rectangle{ZP, Point{w, h}}} + return &RGBA{buf, 4 * w, r} } // RGBA64 is an in-memory image of RGBA64Color values. @@ -219,9 +220,10 @@ func (p *RGBA64) Opaque() bool { } // NewRGBA64 returns a new RGBA64 with the given width and height. -func NewRGBA64(w, h int) *RGBA64 { +func NewRGBA64(r Rectangle) *RGBA64 { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) - return &RGBA64{pix, 8 * w, Rectangle{ZP, Point{w, h}}} + return &RGBA64{pix, 8 * w, r} } // NRGBA is an in-memory image of NRGBAColor values. @@ -307,9 +309,10 @@ func (p *NRGBA) Opaque() bool { } // NewNRGBA returns a new NRGBA with the given width and height. -func NewNRGBA(w, h int) *NRGBA { +func NewNRGBA(r Rectangle) *NRGBA { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 4*w*h) - return &NRGBA{pix, 4 * w, Rectangle{ZP, Point{w, h}}} + return &NRGBA{pix, 4 * w, r} } // NRGBA64 is an in-memory image of NRGBA64Color values. @@ -408,9 +411,10 @@ func (p *NRGBA64) Opaque() bool { } // NewNRGBA64 returns a new NRGBA64 with the given width and height. -func NewNRGBA64(w, h int) *NRGBA64 { +func NewNRGBA64(r Rectangle) *NRGBA64 { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) - return &NRGBA64{pix, 8 * w, Rectangle{ZP, Point{w, h}}} + return &NRGBA64{pix, 8 * w, r} } // Alpha is an in-memory image of AlphaColor values. @@ -489,9 +493,10 @@ func (p *Alpha) Opaque() bool { } // NewAlpha returns a new Alpha with the given width and height. -func NewAlpha(w, h int) *Alpha { +func NewAlpha(r Rectangle) *Alpha { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) - return &Alpha{pix, 1 * w, Rectangle{ZP, Point{w, h}}} + return &Alpha{pix, 1 * w, r} } // Alpha16 is an in-memory image of Alpha16Color values. @@ -573,9 +578,10 @@ func (p *Alpha16) Opaque() bool { } // NewAlpha16 returns a new Alpha16 with the given width and height. -func NewAlpha16(w, h int) *Alpha16 { +func NewAlpha16(r Rectangle) *Alpha16 { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) - return &Alpha16{pix, 2 * w, Rectangle{ZP, Point{w, h}}} + return &Alpha16{pix, 2 * w, r} } // Gray is an in-memory image of GrayColor values. @@ -641,9 +647,10 @@ func (p *Gray) Opaque() bool { } // NewGray returns a new Gray with the given width and height. -func NewGray(w, h int) *Gray { +func NewGray(r Rectangle) *Gray { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) - return &Gray{pix, 1 * w, Rectangle{ZP, Point{w, h}}} + return &Gray{pix, 1 * w, r} } // Gray16 is an in-memory image of Gray16Color values. @@ -712,9 +719,10 @@ func (p *Gray16) Opaque() bool { } // NewGray16 returns a new Gray16 with the given width and height. -func NewGray16(w, h int) *Gray16 { +func NewGray16(r Rectangle) *Gray16 { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) - return &Gray16{pix, 2 * w, Rectangle{ZP, Point{w, h}}} + return &Gray16{pix, 2 * w, r} } // A PalettedColorModel represents a fixed palette of at most 256 colors. @@ -858,7 +866,8 @@ func (p *Paletted) Opaque() bool { } // NewPaletted returns a new Paletted with the given width, height and palette. -func NewPaletted(w, h int, m PalettedColorModel) *Paletted { +func NewPaletted(r Rectangle, m PalettedColorModel) *Paletted { + w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) - return &Paletted{pix, 1 * w, Rectangle{ZP, Point{w, h}}, m} + return &Paletted{pix, 1 * w, r, m} } diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go index a368e71e6..e23a3c259 100644 --- a/src/pkg/image/image_test.go +++ b/src/pkg/image/image_test.go @@ -23,15 +23,15 @@ func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { func TestImage(t *testing.T) { testImage := []image{ - NewRGBA(10, 10), - NewRGBA64(10, 10), - NewNRGBA(10, 10), - NewNRGBA64(10, 10), - NewAlpha(10, 10), - NewAlpha16(10, 10), - NewGray(10, 10), - NewGray16(10, 10), - NewPaletted(10, 10, PalettedColorModel{ + NewRGBA(Rect(0, 0, 10, 10)), + NewRGBA64(Rect(0, 0, 10, 10)), + NewNRGBA(Rect(0, 0, 10, 10)), + NewNRGBA64(Rect(0, 0, 10, 10)), + NewAlpha(Rect(0, 0, 10, 10)), + NewAlpha16(Rect(0, 0, 10, 10)), + NewGray(Rect(0, 0, 10, 10)), + NewGray16(Rect(0, 0, 10, 10)), + NewPaletted(Rect(0, 0, 10, 10), PalettedColorModel{ Transparent, Opaque, }), @@ -96,10 +96,10 @@ func Test16BitsPerColorChannel(t *testing.T) { } } testImage := []image{ - NewRGBA64(10, 10), - NewNRGBA64(10, 10), - NewAlpha16(10, 10), - NewGray16(10, 10), + NewRGBA64(Rect(0, 0, 10, 10)), + NewNRGBA64(Rect(0, 0, 10, 10)), + NewAlpha16(Rect(0, 0, 10, 10)), + NewGray16(Rect(0, 0, 10, 10)), } for _, m := range testImage { m.Set(1, 2, NRGBA64Color{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha. diff --git a/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go index 3f22c5271..af69cfcec 100644 --- a/src/pkg/image/jpeg/reader.go +++ b/src/pkg/image/jpeg/reader.go @@ -199,7 +199,7 @@ func (d *decoder) processDQT(n int) os.Error { // makeImg allocates and initializes the destination image. func (d *decoder) makeImg(h0, v0, mxx, myy int) { if d.nComp == nGrayComponent { - m := image.NewGray(8*mxx, 8*myy) + m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy)) d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray) return } diff --git a/src/pkg/image/jpeg/writer_test.go b/src/pkg/image/jpeg/writer_test.go index 7aec70f01..44c045ed0 100644 --- a/src/pkg/image/jpeg/writer_test.go +++ b/src/pkg/image/jpeg/writer_test.go @@ -90,7 +90,7 @@ func TestWriter(t *testing.T) { func BenchmarkEncodeRGBOpaque(b *testing.B) { b.StopTimer() - img := image.NewRGBA(640, 480) + img := image.NewRGBA(image.Rect(0, 0, 640, 480)) // Set all pixels to 0xFF alpha to force opaque mode. bo := img.Bounds() rnd := rand.New(rand.NewSource(123)) diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go index aa023741d..19cb248c1 100644 --- a/src/pkg/image/png/reader.go +++ b/src/pkg/image/png/reader.go @@ -4,11 +4,12 @@ // Package png implements a PNG image decoder and encoder. // -// The PNG specification is at http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html +// The PNG specification is at http://www.w3.org/TR/PNG/. package png import ( "compress/zlib" + "encoding/binary" "fmt" "hash" "hash/crc32" @@ -61,6 +62,7 @@ const ( // chunks must appear in that order. There may be multiple IDAT chunks, and // IDAT chunks must be sequential (i.e. they may not have any other chunks // between them). +// http://www.w3.org/TR/PNG/#5ChunkOrdering const ( dsStart = iota dsSeenIHDR @@ -71,19 +73,16 @@ const ( const pngHeader = "\x89PNG\r\n\x1a\n" -type imgOrErr struct { - img image.Image - err os.Error -} - type decoder struct { + r io.Reader + img image.Image + crc hash.Hash32 width, height int depth int palette image.PalettedColorModel cb int stage int - idatWriter io.WriteCloser - idatDone chan imgOrErr + idatLength uint32 tmp [3 * 256]byte } @@ -94,23 +93,11 @@ func (e FormatError) String() string { return "png: invalid format: " + string(e var chunkOrderError = FormatError("chunk out of order") -// An IDATDecodingError wraps an inner error (such as a ZLIB decoding error) encountered while processing an IDAT chunk. -type IDATDecodingError struct { - Err os.Error -} - -func (e IDATDecodingError) String() string { return "png: IDAT decoding error: " + e.Err.String() } - // An UnsupportedError reports that the input uses a valid but unimplemented PNG feature. type UnsupportedError string func (e UnsupportedError) String() string { return "png: unsupported feature: " + string(e) } -// Big-endian. -func parseUint32(b []uint8) uint32 { - return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) -} - func abs(x int) int { if x < 0 { return -x @@ -125,20 +112,19 @@ func min(a, b int) int { return b } -func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Error { +func (d *decoder) parseIHDR(length uint32) os.Error { if length != 13 { return FormatError("bad IHDR length") } - _, err := io.ReadFull(r, d.tmp[0:13]) - if err != nil { + if _, err := io.ReadFull(d.r, d.tmp[:13]); err != nil { return err } - crc.Write(d.tmp[0:13]) + d.crc.Write(d.tmp[:13]) if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 { return UnsupportedError("compression, filter or interlace method") } - w := int32(parseUint32(d.tmp[0:4])) - h := int32(parseUint32(d.tmp[4:8])) + w := int32(binary.BigEndian.Uint32(d.tmp[0:4])) + h := int32(binary.BigEndian.Uint32(d.tmp[4:8])) if w < 0 || h < 0 { return FormatError("negative dimension") } @@ -199,19 +185,19 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", d.tmp[8], d.tmp[9])) } d.width, d.height = int(w), int(h) - return nil + return d.verifyChecksum() } -func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Error { +func (d *decoder) parsePLTE(length uint32) os.Error { np := int(length / 3) // The number of palette entries. if length%3 != 0 || np <= 0 || np > 256 || np > 1<<uint(d.depth) { return FormatError("bad PLTE length") } - n, err := io.ReadFull(r, d.tmp[0:3*np]) + n, err := io.ReadFull(d.r, d.tmp[:3*np]) if err != nil { return err } - crc.Write(d.tmp[0:n]) + d.crc.Write(d.tmp[:n]) switch d.cb { case cbP1, cbP2, cbP4, cbP8: d.palette = image.PalettedColorModel(make([]image.Color, np)) @@ -224,18 +210,18 @@ func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Erro default: return FormatError("PLTE, color type mismatch") } - return nil + return d.verifyChecksum() } -func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Error { +func (d *decoder) parsetRNS(length uint32) os.Error { if length > 256 { return FormatError("bad tRNS length") } - n, err := io.ReadFull(r, d.tmp[0:length]) + n, err := io.ReadFull(d.r, d.tmp[:length]) if err != nil { return err } - crc.Write(d.tmp[0:n]) + d.crc.Write(d.tmp[:n]) switch d.cb { case cbG8, cbG16: return UnsupportedError("grayscale transparency") @@ -252,7 +238,7 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro case cbGA8, cbGA16, cbTCA8, cbTCA16: return FormatError("tRNS, color type mismatch") } - return nil + return d.verifyChecksum() } // The Paeth filter function, as per the PNG specification. @@ -269,8 +255,46 @@ func paeth(a, b, c uint8) uint8 { return c } -func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { - r, err := zlib.NewReader(idat) +// Read presents one or more IDAT chunks as one continuous stream (minus the +// intermediate chunk headers and footers). If the PNG data looked like: +// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2 +// then this reader presents xxxyy. For well-formed PNG data, the decoder state +// immediately before the first Read call is that d.r is positioned between the +// first IDAT and xxx, and the decoder state immediately after the last Read +// call is that d.r is positioned between yy and crc1. +func (d *decoder) Read(p []byte) (int, os.Error) { + if len(p) == 0 { + return 0, nil + } + for d.idatLength == 0 { + // We have exhausted an IDAT chunk. Verify the checksum of that chunk. + if err := d.verifyChecksum(); err != nil { + return 0, err + } + // Read the length and chunk type of the next chunk, and check that + // it is an IDAT chunk. + if _, err := io.ReadFull(d.r, d.tmp[:8]); err != nil { + return 0, err + } + d.idatLength = binary.BigEndian.Uint32(d.tmp[:4]) + if string(d.tmp[4:8]) != "IDAT" { + return 0, FormatError("not enough pixel data") + } + d.crc.Reset() + d.crc.Write(d.tmp[4:8]) + } + if int(d.idatLength) < 0 { + return 0, UnsupportedError("IDAT chunk length overflow") + } + n, err := d.r.Read(p[:min(len(p), int(d.idatLength))]) + d.crc.Write(p[:n]) + d.idatLength -= uint32(n) + return n, err +} + +// decode decodes the IDAT data into an image. +func (d *decoder) decode() (image.Image, os.Error) { + r, err := zlib.NewReader(d) if err != nil { return nil, err } @@ -290,40 +314,40 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { switch d.cb { case cbG1, cbG2, cbG4, cbG8: bitsPerPixel = d.depth - gray = image.NewGray(d.width, d.height) + gray = image.NewGray(image.Rect(0, 0, d.width, d.height)) img = gray case cbGA8: bitsPerPixel = 16 - nrgba = image.NewNRGBA(d.width, d.height) + nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) img = nrgba case cbTC8: bitsPerPixel = 24 - rgba = image.NewRGBA(d.width, d.height) + rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height)) img = rgba case cbP1, cbP2, cbP4, cbP8: bitsPerPixel = d.depth - paletted = image.NewPaletted(d.width, d.height, d.palette) + paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette) img = paletted maxPalette = uint8(len(d.palette) - 1) case cbTCA8: bitsPerPixel = 32 - nrgba = image.NewNRGBA(d.width, d.height) + nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) img = nrgba case cbG16: bitsPerPixel = 16 - gray16 = image.NewGray16(d.width, d.height) + gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height)) img = gray16 case cbGA16: bitsPerPixel = 32 - nrgba64 = image.NewNRGBA64(d.width, d.height) + nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height)) img = nrgba64 case cbTC16: bitsPerPixel = 48 - rgba64 = image.NewRGBA64(d.width, d.height) + rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height)) img = rgba64 case cbTCA16: bitsPerPixel = 64 - nrgba64 = image.NewNRGBA64(d.width, d.height) + nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height)) img = nrgba64 } bytesPerPixel := (bitsPerPixel + 7) / 8 @@ -495,147 +519,100 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) { if err != os.EOF { return nil, FormatError(err.String()) } - if n != 0 { + if n != 0 || d.idatLength != 0 { return nil, FormatError("too much pixel data") } return img, nil } -func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error { - // There may be more than one IDAT chunk, but their contents must be - // treated as if it was one continuous stream (to the zlib decoder). - // We bring up an io.Pipe and write the IDAT chunks into the pipe as - // we see them, and decode the stream in a separate go-routine, which - // signals its completion (successful or not) via a channel. - if d.idatWriter == nil { - pr, pw := io.Pipe() - d.idatWriter = pw - d.idatDone = make(chan imgOrErr) - go func() { - img, err := d.idatReader(pr) - if err == os.EOF { - err = FormatError("too little IDAT") - } - pr.CloseWithError(FormatError("too much IDAT")) - d.idatDone <- imgOrErr{img, err} - }() - } - var buf [4096]byte - for length > 0 { - n, err1 := r.Read(buf[0:min(len(buf), int(length))]) - // We delay checking err1. It is possible to get n bytes and an error, - // but if the n bytes themselves contain a FormatError, for example, we - // want to report that error, and not the one that made the Read stop. - n, err2 := d.idatWriter.Write(buf[0:n]) - if err2 != nil { - return err2 - } - if err1 != nil { - return err1 - } - crc.Write(buf[0:n]) - length -= uint32(n) +func (d *decoder) parseIDAT(length uint32) (err os.Error) { + d.idatLength = length + d.img, err = d.decode() + if err != nil { + return err } - return nil + return d.verifyChecksum() } -func (d *decoder) parseIEND(r io.Reader, crc hash.Hash32, length uint32) os.Error { +func (d *decoder) parseIEND(length uint32) os.Error { if length != 0 { return FormatError("bad IEND length") } - return nil + return d.verifyChecksum() } -func (d *decoder) parseChunk(r io.Reader) os.Error { - // Read the length. - n, err := io.ReadFull(r, d.tmp[0:4]) - if err == os.EOF { - return io.ErrUnexpectedEOF - } +func (d *decoder) parseChunk() os.Error { + // Read the length and chunk type. + n, err := io.ReadFull(d.r, d.tmp[:8]) if err != nil { return err } - length := parseUint32(d.tmp[0:4]) - - // Read the chunk type. - n, err = io.ReadFull(r, d.tmp[0:4]) - if err == os.EOF { - return io.ErrUnexpectedEOF - } - if err != nil { - return err - } - crc := crc32.NewIEEE() - crc.Write(d.tmp[0:4]) + length := binary.BigEndian.Uint32(d.tmp[:4]) + d.crc.Reset() + d.crc.Write(d.tmp[4:8]) // Read the chunk data. - switch string(d.tmp[0:4]) { + switch string(d.tmp[4:8]) { case "IHDR": if d.stage != dsStart { return chunkOrderError } d.stage = dsSeenIHDR - err = d.parseIHDR(r, crc, length) + return d.parseIHDR(length) case "PLTE": if d.stage != dsSeenIHDR { return chunkOrderError } d.stage = dsSeenPLTE - err = d.parsePLTE(r, crc, length) + return d.parsePLTE(length) case "tRNS": if d.stage != dsSeenPLTE { return chunkOrderError } - err = d.parsetRNS(r, crc, length) + return d.parsetRNS(length) case "IDAT": if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) { return chunkOrderError } d.stage = dsSeenIDAT - err = d.parseIDAT(r, crc, length) + return d.parseIDAT(length) case "IEND": if d.stage != dsSeenIDAT { return chunkOrderError } d.stage = dsSeenIEND - err = d.parseIEND(r, crc, length) - default: - // Ignore this chunk (of a known length). - var ignored [4096]byte - for length > 0 { - n, err = io.ReadFull(r, ignored[0:min(len(ignored), int(length))]) - if err != nil { - return err - } - crc.Write(ignored[0:n]) - length -= uint32(n) - } + return d.parseIEND(length) } - if err != nil { - return err + // Ignore this chunk (of a known length). + var ignored [4096]byte + for length > 0 { + n, err = io.ReadFull(d.r, ignored[:min(len(ignored), int(length))]) + if err != nil { + return err + } + d.crc.Write(ignored[:n]) + length -= uint32(n) } + return d.verifyChecksum() +} - // Read the checksum. - n, err = io.ReadFull(r, d.tmp[0:4]) - if err == os.EOF { - return io.ErrUnexpectedEOF - } - if err != nil { +func (d *decoder) verifyChecksum() os.Error { + if _, err := io.ReadFull(d.r, d.tmp[:4]); err != nil { return err } - if parseUint32(d.tmp[0:4]) != crc.Sum32() { + if binary.BigEndian.Uint32(d.tmp[:4]) != d.crc.Sum32() { return FormatError("invalid checksum") } return nil } -func (d *decoder) checkHeader(r io.Reader) os.Error { - _, err := io.ReadFull(r, d.tmp[0:8]) +func (d *decoder) checkHeader() os.Error { + _, err := io.ReadFull(d.r, d.tmp[:len(pngHeader)]) if err != nil { return err } - if string(d.tmp[0:8]) != pngHeader { + if string(d.tmp[:len(pngHeader)]) != pngHeader { return FormatError("not a PNG file") } return nil @@ -644,42 +621,45 @@ func (d *decoder) checkHeader(r io.Reader) os.Error { // Decode reads a PNG image from r and returns it as an image.Image. // The type of Image returned depends on the PNG contents. func Decode(r io.Reader) (image.Image, os.Error) { - var d decoder - err := d.checkHeader(r) - if err != nil { - return nil, err + d := &decoder{ + r: r, + crc: crc32.NewIEEE(), } - for d.stage != dsSeenIEND { - err = d.parseChunk(r) - if err != nil { - break + if err := d.checkHeader(); err != nil { + if err == os.EOF { + err = io.ErrUnexpectedEOF } + return nil, err } - var img image.Image - if d.idatWriter != nil { - d.idatWriter.Close() - ie := <-d.idatDone - if err == nil { - img, err = ie.img, ie.err + for d.stage != dsSeenIEND { + if err := d.parseChunk(); err != nil { + if err == os.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err } } - if err != nil { - return nil, err - } - return img, nil + return d.img, nil } // DecodeConfig returns the color model and dimensions of a PNG image without // decoding the entire image. func DecodeConfig(r io.Reader) (image.Config, os.Error) { - var d decoder - err := d.checkHeader(r) - if err != nil { + d := &decoder{ + r: r, + crc: crc32.NewIEEE(), + } + if err := d.checkHeader(); err != nil { + if err == os.EOF { + err = io.ErrUnexpectedEOF + } return image.Config{}, err } for { - err = d.parseChunk(r) - if err != nil { + if err := d.parseChunk(); err != nil { + if err == os.EOF { + err = io.ErrUnexpectedEOF + } return image.Config{}, err } if d.stage == dsSeenIHDR && d.cb != cbP8 { diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go index 208843190..1b7c2de71 100644 --- a/src/pkg/image/png/reader_test.go +++ b/src/pkg/image/png/reader_test.go @@ -262,7 +262,7 @@ func TestReaderError(t *testing.T) { t.Errorf("decoding %s: %s, want %s", tt.file, err, tt.err) } if img != nil { - t.Errorf("decoding %s: have image + error") + t.Errorf("decoding %s: have image + error", tt.file) } } } diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go index f9556a0f9..2dc5537cc 100644 --- a/src/pkg/image/png/writer.go +++ b/src/pkg/image/png/writer.go @@ -160,10 +160,6 @@ func (e *encoder) maybeWritetRNS(p image.PalettedColorModel) { // // This method should only be called from writeIDATs (via writeImage). // No other code should treat an encoder as an io.Writer. -// -// Note that, because the zlib Reader may involve an io.Pipe, e.Write calls may -// occur on a separate go-routine than the e.writeIDATs call, and care should be -// taken that e's state (such as its tmp buffer) is not modified concurrently. func (e *encoder) Write(b []byte) (int, os.Error) { e.writeChunk(b, "IDAT") if e.err != nil { diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go index 046aad9d2..a3864e096 100644 --- a/src/pkg/image/png/writer_test.go +++ b/src/pkg/image/png/writer_test.go @@ -82,7 +82,7 @@ func TestWriter(t *testing.T) { } func TestSubImage(t *testing.T) { - m0 := image.NewRGBA(256, 256) + m0 := image.NewRGBA(image.Rect(0, 0, 256, 256)) for y := 0; y < 256; y++ { for x := 0; x < 256; x++ { m0.Set(x, y, image.RGBAColor{uint8(x), uint8(y), 0, 255}) @@ -103,7 +103,7 @@ func TestSubImage(t *testing.T) { func BenchmarkEncodePaletted(b *testing.B) { b.StopTimer() - img := image.NewPaletted(640, 480, + img := image.NewPaletted(image.Rect(0, 0, 640, 480), []image.Color{ image.RGBAColor{0, 0, 0, 255}, image.RGBAColor{255, 255, 255, 255}, @@ -117,7 +117,7 @@ func BenchmarkEncodePaletted(b *testing.B) { func BenchmarkEncodeRGBOpaque(b *testing.B) { b.StopTimer() - img := image.NewRGBA(640, 480) + img := image.NewRGBA(image.Rect(0, 0, 640, 480)) // Set all pixels to 0xFF alpha to force opaque mode. bo := img.Bounds() for y := bo.Min.Y; y < bo.Max.Y; y++ { @@ -137,7 +137,7 @@ func BenchmarkEncodeRGBOpaque(b *testing.B) { func BenchmarkEncodeRGBA(b *testing.B) { b.StopTimer() - img := image.NewRGBA(640, 480) + img := image.NewRGBA(image.Rect(0, 0, 640, 480)) if img.Opaque() { panic("expected image to not be opaque") } diff --git a/src/pkg/image/tiff/reader.go b/src/pkg/image/tiff/reader.go index c96399221..c1c0a1b16 100644 --- a/src/pkg/image/tiff/reader.go +++ b/src/pkg/image/tiff/reader.go @@ -378,13 +378,13 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { switch d.mode { case mGray, mGrayInvert: - img = image.NewGray(d.config.Width, d.config.Height) + img = image.NewGray(image.Rect(0, 0, d.config.Width, d.config.Height)) case mPaletted: - img = image.NewPaletted(d.config.Width, d.config.Height, d.palette) + img = image.NewPaletted(image.Rect(0, 0, d.config.Width, d.config.Height), d.palette) case mNRGBA: - img = image.NewNRGBA(d.config.Width, d.config.Height) + img = image.NewNRGBA(image.Rect(0, 0, d.config.Width, d.config.Height)) case mRGB, mRGBA: - img = image.NewRGBA(d.config.Width, d.config.Height) + img = image.NewRGBA(image.Rect(0, 0, d.config.Width, d.config.Height)) } for i := 0; i < numStrips; i++ { diff --git a/src/pkg/index/suffixarray/qsufsort.go b/src/pkg/index/suffixarray/qsufsort.go index 30c110442..f4ec3a103 100644 --- a/src/pkg/index/suffixarray/qsufsort.go +++ b/src/pkg/index/suffixarray/qsufsort.go @@ -26,7 +26,7 @@ package suffixarray import "sort" -func qsufsort(data []byte) []int { +func qsufsort(data []byte) []int32 { // initial sorting by first byte of suffix sa := sortedByFirstByte(data) if len(sa) < 2 { @@ -37,22 +37,22 @@ func qsufsort(data []byte) []int { inv := initGroups(sa, data) // the index starts 1-ordered - sufSortable := &suffixSortable{sa, inv, 1} + sufSortable := &suffixSortable{sa: sa, inv: inv, h: 1} - for sa[0] > -len(sa) { // until all suffixes are one big sorted group + for int(sa[0]) > -len(sa) { // until all suffixes are one big sorted group // The suffixes are h-ordered, make them 2*h-ordered pi := 0 // pi is first position of first group sl := 0 // sl is negated length of sorted groups for pi < len(sa) { - if s := sa[pi]; s < 0 { // if pi starts sorted group + if s := int(sa[pi]); s < 0 { // if pi starts sorted group pi -= s // skip over sorted group sl += s // add negated length to sl } else { // if pi starts unsorted group if sl != 0 { - sa[pi+sl] = sl // combine sorted groups before pi + sa[pi+sl] = int32(sl) // combine sorted groups before pi sl = 0 } - pk := inv[s] + 1 // pk-1 is last position of unsorted group + pk := int(inv[s]) + 1 // pk-1 is last position of unsorted group sufSortable.sa = sa[pi:pk] sort.Sort(sufSortable) sufSortable.updateGroups(pi) @@ -60,19 +60,19 @@ func qsufsort(data []byte) []int { } } if sl != 0 { // if the array ends with a sorted group - sa[pi+sl] = sl // combine sorted groups at end of sa + sa[pi+sl] = int32(sl) // combine sorted groups at end of sa } sufSortable.h *= 2 // double sorted depth } for i := range sa { // reconstruct suffix array from inverse - sa[inv[i]] = i + sa[inv[i]] = int32(i) } return sa } -func sortedByFirstByte(data []byte) []int { +func sortedByFirstByte(data []byte) []int32 { // total byte counts var count [256]int for _, b := range data { @@ -84,17 +84,17 @@ func sortedByFirstByte(data []byte) []int { count[b], sum = sum, count[b]+sum } // iterate through bytes, placing index into the correct spot in sa - sa := make([]int, len(data)) + sa := make([]int32, len(data)) for i, b := range data { - sa[count[b]] = i + sa[count[b]] = int32(i) count[b]++ } return sa } -func initGroups(sa []int, data []byte) []int { +func initGroups(sa []int32, data []byte) []int32 { // label contiguous same-letter groups with the same group number - inv := make([]int, len(data)) + inv := make([]int32, len(data)) prevGroup := len(sa) - 1 groupByte := data[sa[prevGroup]] for i := len(sa) - 1; i >= 0; i-- { @@ -105,7 +105,7 @@ func initGroups(sa []int, data []byte) []int { groupByte = b prevGroup = i } - inv[sa[i]] = prevGroup + inv[sa[i]] = int32(prevGroup) if prevGroup == 0 { sa[0] = -1 } @@ -120,9 +120,9 @@ func initGroups(sa []int, data []byte) []int { if data[sa[i]] == lastByte && s == -1 { s = i } - if sa[i] == len(sa)-1 { + if int(sa[i]) == len(sa)-1 { sa[i], sa[s] = sa[s], sa[i] - inv[sa[s]] = s + inv[sa[s]] = int32(s) sa[s] = -1 // mark it as an isolated sorted group break } @@ -132,9 +132,10 @@ func initGroups(sa []int, data []byte) []int { } type suffixSortable struct { - sa []int - inv []int - h int + sa []int32 + inv []int32 + h int32 + buf []int // common scratch space } func (x *suffixSortable) Len() int { return len(x.sa) } @@ -142,7 +143,7 @@ func (x *suffixSortable) Less(i, j int) bool { return x.inv[x.sa[i]+x.h] < x.inv func (x *suffixSortable) Swap(i, j int) { x.sa[i], x.sa[j] = x.sa[j], x.sa[i] } func (x *suffixSortable) updateGroups(offset int) { - bounds := make([]int, 0, 4) + bounds := x.buf[0:0] group := x.inv[x.sa[0]+x.h] for i := 1; i < len(x.sa); i++ { if g := x.inv[x.sa[i]+x.h]; g > group { @@ -151,12 +152,13 @@ func (x *suffixSortable) updateGroups(offset int) { } } bounds = append(bounds, len(x.sa)) + x.buf = bounds // update the group numberings after all new groups are determined prev := 0 for _, b := range bounds { for i := prev; i < b; i++ { - x.inv[x.sa[i]] = offset + b - 1 + x.inv[x.sa[i]] = int32(offset + b - 1) } if b-prev == 1 { x.sa[prev] = -1 diff --git a/src/pkg/index/suffixarray/suffixarray.go b/src/pkg/index/suffixarray/suffixarray.go index 82e98d2ef..cff7daa9d 100644 --- a/src/pkg/index/suffixarray/suffixarray.go +++ b/src/pkg/index/suffixarray/suffixarray.go @@ -18,14 +18,17 @@ package suffixarray import ( "bytes" - "regexp" + "exp/regexp" + "gob" + "io" + "os" "sort" ) // Index implements a suffix array for fast substring search. type Index struct { data []byte - sa []int // suffix array for data + sa []int32 // suffix array for data; len(sa) == len(data) } // New creates a new Index for data. @@ -34,6 +37,76 @@ func New(data []byte) *Index { return &Index{data, qsufsort(data)} } +// Read and Write slice the data into successive portions of length gobN, +// so gob can allocate smaller buffers for its I/O. +const gobN = 1 << 16 // slightly better than say 1 << 20 (BenchmarkSaveRestore) + +// Read reads the index from r into x; x must not be nil. +func (x *Index) Read(r io.Reader) os.Error { + d := gob.NewDecoder(r) + var n int + if err := d.Decode(&n); err != nil { + return err + } + if 2*n < cap(x.data) || cap(x.data) < n { + // new data is significantly smaller or larger then + // existing buffers - allocate new ones + x.data = make([]byte, n) + x.sa = make([]int32, n) + } else { + // re-use existing buffers + x.data = x.data[0:n] + x.sa = x.sa[0:n] + } + for i := 0; i < n; { + j := i + gobN + if j > n { + j = n + } + // data holds next piece of x.data; its length is updated by Decode + data := x.data[i:j] + if err := d.Decode(&data); err != nil { + return err + } + if len(data) != j-i { + return os.NewError("suffixarray.Read: inconsistent data format") + } + // sa holds next piece of x.data; its length is updated by Decode + sa := x.sa[i:j] + if err := d.Decode(&sa); err != nil { + return err + } + if len(sa) != j-i { + return os.NewError("suffixarray.Read: inconsistent data format") + } + i = j + } + return nil +} + +// Write writes the index x to w. +func (x *Index) Write(w io.Writer) os.Error { + e := gob.NewEncoder(w) + n := len(x.data) + if err := e.Encode(n); err != nil { + return err + } + for i := 0; i < n; { + j := i + gobN + if j > n { + j = n + } + if err := e.Encode(x.data[i:j]); err != nil { + return err + } + if err := e.Encode(x.sa[i:j]); err != nil { + return err + } + i = j + } + return nil +} + // Bytes returns the data over which the index was created. // It must not be modified. // @@ -47,7 +120,7 @@ func (x *Index) at(i int) []byte { // lookupAll returns a slice into the matching region of the index. // The runtime is O(log(N)*len(s)). -func (x *Index) lookupAll(s []byte) []int { +func (x *Index) lookupAll(s []byte) []int32 { // find matching suffix index range [i:j] // find the first index where s would be the prefix i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 }) @@ -65,12 +138,15 @@ func (x *Index) lookupAll(s []byte) []int { func (x *Index) Lookup(s []byte, n int) (result []int) { if len(s) > 0 && n != 0 { matches := x.lookupAll(s) - if len(matches) < n || n < 0 { + if n < 0 || len(matches) < n { n = len(matches) } + // 0 <= n <= len(matches) if n > 0 { result = make([]int, n) - copy(result, matches) + for i, x := range matches[0:n] { + result[i] = int(x) + } } } return diff --git a/src/pkg/index/suffixarray/suffixarray_test.go b/src/pkg/index/suffixarray/suffixarray_test.go index 023748500..9b4d89f42 100644 --- a/src/pkg/index/suffixarray/suffixarray_test.go +++ b/src/pkg/index/suffixarray/suffixarray_test.go @@ -6,7 +6,8 @@ package suffixarray import ( "bytes" - "regexp" + "exp/regexp" + "rand" "sort" "strings" "testing" @@ -213,7 +214,33 @@ func (a *index) at(i int) []byte { return a.data[a.sa[i]:] } func testConstruction(t *testing.T, tc *testCase, x *Index) { if !sort.IsSorted((*index)(x)) { - t.Errorf("testConstruction failed %s", tc.name) + t.Errorf("failed testConstruction %s", tc.name) + } +} + +func equal(x, y *Index) bool { + if !bytes.Equal(x.data, y.data) { + return false + } + for i, j := range x.sa { + if j != y.sa[i] { + return false + } + } + return true +} + +func testSaveRestore(t *testing.T, tc *testCase, x *Index) { + var buf bytes.Buffer + if err := x.Write(&buf); err != nil { + t.Errorf("failed writing index %s (%s)", tc.name, err) + } + var y Index + if err := y.Read(&buf); err != nil { + t.Errorf("failed reading index %s (%s)", tc.name, err) + } + if !equal(x, &y) { + t.Errorf("restored index doesn't match saved index %s", tc.name) } } @@ -221,6 +248,7 @@ func TestIndex(t *testing.T) { for _, tc := range testCases { x := New([]byte(tc.source)) testConstruction(t, &tc, x) + testSaveRestore(t, &tc, x) testLookups(t, &tc, x, 0) testLookups(t, &tc, x, 1) testLookups(t, &tc, x, 10) @@ -228,3 +256,21 @@ func TestIndex(t *testing.T) { testLookups(t, &tc, x, -1) } } + +func BenchmarkSaveRestore(b *testing.B) { + b.StopTimer() + r := rand.New(rand.NewSource(0x5a77a1)) // guarantee always same sequence + data := make([]byte, 10<<20) // 10MB index data + for i := range data { + data[i] = byte(r.Intn(256)) + } + x := New(data) + testSaveRestore(nil, nil, x) // verify correctness + buf := bytes.NewBuffer(make([]byte, len(data))) // avoid frequent growing + b.StartTimer() + for i := 0; i < b.N; i++ { + x.Write(buf) + var y Index + y.Read(buf) + } +} diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go index b7129f984..5ac01e859 100644 --- a/src/pkg/json/decode.go +++ b/src/pkg/json/decode.go @@ -22,17 +22,20 @@ import ( // Unmarshal parses the JSON-encoded data and stores the result // in the value pointed to by v. // -// Unmarshal traverses the value v recursively. -// If an encountered value implements the Unmarshaler interface, -// Unmarshal calls its UnmarshalJSON method with a well-formed -// JSON encoding. -// -// Otherwise, Unmarshal uses the inverse of the encodings that +// Unmarshal uses the inverse of the encodings that // Marshal uses, allocating maps, slices, and pointers as necessary, // with the following additional rules: // -// To unmarshal a JSON value into a nil interface value, the -// type stored in the interface value is one of: +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into an interface value, Unmarshal unmarshals +// the JSON into the concrete value contained in the interface value. +// If the interface value is nil, that is, has no concrete value stored in it, +// Unmarshal stores one of these in the interface value: // // bool, for JSON booleans // float64, for JSON numbers @@ -250,8 +253,8 @@ func (d *decodeState) value(v reflect.Value) { // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. -// if wantptr is true, indirect stops at the last pointer. -func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. @@ -277,7 +280,7 @@ func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, refl break } - if pv.Elem().Kind() != reflect.Ptr && wantptr && pv.CanSet() && !isUnmarshaler { + if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { return nil, pv } if pv.IsNil() { @@ -391,11 +394,6 @@ func (d *decodeState) array(v reflect.Value) { } } -// matchName returns true if key should be written to a field named name. -func matchName(key, name string) bool { - return strings.ToLower(key) == strings.ToLower(name) -} - // object consumes an object from d.data[d.off-1:], decoding into the value v. // the first byte of the object ('{') has been read already. func (d *decodeState) object(v reflect.Value) { @@ -485,24 +483,31 @@ func (d *decodeState) object(v reflect.Value) { var f reflect.StructField var ok bool st := sv.Type() - // First try for field with that tag. - if isValidTag(key) { - for i := 0; i < sv.NumField(); i++ { - f = st.Field(i) - tagName, _ := parseTag(f.Tag.Get("json")) - if tagName == key { - ok = true - break - } + for i := 0; i < sv.NumField(); i++ { + sf := st.Field(i) + tag := sf.Tag.Get("json") + if tag == "-" { + // Pretend this field doesn't exist. + continue + } + // First, tag match + tagName, _ := parseTag(tag) + if tagName == key { + f = sf + ok = true + break // no better match possible + } + // Second, exact field name match + if sf.Name == key { + f = sf + ok = true + } + // Third, case-insensitive field name match, + // but only if a better match hasn't already been seen + if !ok && strings.ToLower(sf.Name) == strings.ToLower(key) { + f = sf + ok = true } - } - if !ok { - // Second, exact match. - f, ok = st.FieldByName(key) - } - if !ok { - // Third, case-insensitive match. - f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) } // Extract value; name must be exported. diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go index c6d4fa059..2c7cbc4a2 100644 --- a/src/pkg/json/decode_test.go +++ b/src/pkg/json/decode_test.go @@ -15,6 +15,7 @@ import ( type T struct { X string Y int + Z int `json:"-"` } type tx struct { @@ -68,6 +69,9 @@ var unmarshalTests = []unmarshalTest{ {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}}, {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + // Z has a "-" tag. + {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + // syntax errors {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go index 5b4e616f7..46abe4360 100644 --- a/src/pkg/json/encode.go +++ b/src/pkg/json/encode.go @@ -4,6 +4,9 @@ // Package json implements encoding and decoding of JSON objects as defined in // RFC 4627. +// +// See "JSON and Go" for an introduction to this package: +// http://blog.golang.org/2011/01/json-and-go.html package json import ( @@ -21,8 +24,11 @@ import ( // Marshal returns the JSON encoding of v. // // Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface, -// Marshal calls its MarshalJSON method to produce JSON. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. // // Otherwise, Marshal uses the following type-dependent default encodings: // @@ -37,18 +43,23 @@ import ( // []byte encodes as a base64-encoded string. // // Struct values encode as JSON objects. Each exported struct field -// becomes a member of the object unless the field is empty and its tag -// specifies the "omitempty" option. The empty values are false, 0, any +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any // nil pointer or interface value, and any array, slice, map, or string of // length zero. The object's default key string is the struct field name // but can be specified in the struct field's tag value. The "json" key in // struct field's tag value is the key name, followed by an optional comma // and options. Examples: // -// // Specifies that Field appears in JSON as key "myName" +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". // Field int `json:"myName"` // -// // Specifies that Field appears in JSON as key "myName" and +// // Field appears in JSON as key "myName" and // // the field is omitted from the object if its value is empty, // // as defined above. // Field int `json:"myName,omitempty"` @@ -237,7 +248,7 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { return } - if j, ok := v.Interface().(Marshaler); ok { + if j, ok := v.Interface().(Marshaler); ok && (v.Kind() != reflect.Ptr || !v.IsNil()) { b, err := j.MarshalJSON() if err == nil { // copy JSON into buffer, checking validity. @@ -295,6 +306,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } tag, omitEmpty, quoted := f.Name, false, false if tv := f.Tag.Get("json"); tv != "" { + if tv == "-" { + continue + } name, opts := parseTag(tv) if isValidTag(name) { tag = name diff --git a/src/pkg/json/encode_test.go b/src/pkg/json/encode_test.go index 012e9f143..f85bb6216 100644 --- a/src/pkg/json/encode_test.go +++ b/src/pkg/json/encode_test.go @@ -13,6 +13,7 @@ import ( type Optionals struct { Sr string `json:"sr"` So string `json:"so,omitempty"` + Sw string `json:"-"` Ir int `json:"omitempty"` // actually named omitempty, not an option Io int `json:"io,omitempty"` @@ -33,6 +34,7 @@ var optionalsExpected = `{ func TestOmitEmpty(t *testing.T) { var o Optionals + o.Sw = "something" o.Mr = map[string]interface{}{} o.Mo = map[string]interface{}{} diff --git a/src/pkg/json/stream_test.go b/src/pkg/json/stream_test.go index 6ddaed9fe..ce5a7e6d6 100644 --- a/src/pkg/json/stream_test.go +++ b/src/pkg/json/stream_test.go @@ -120,3 +120,28 @@ func TestRawMessage(t *testing.T) { t.Fatalf("Marshal: have %#q want %#q", b, msg) } } + +func TestNullRawMessage(t *testing.T) { + // TODO(rsc): Should not need the * in *RawMessage + var data struct { + X float64 + Id *RawMessage + Y float32 + } + data.Id = new(RawMessage) + const msg = `{"X":0.1,"Id":null,"Y":0.2}` + err := Unmarshal([]byte(msg), &data) + if err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if data.Id != nil { + t.Fatalf("Raw mismatch: have non-nil, want nil") + } + b, err := Marshal(&data) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if string(b) != msg { + t.Fatalf("Marshal: have %#q want %#q", b, msg) + } +} diff --git a/src/pkg/mail/message_test.go b/src/pkg/mail/message_test.go index e1bcc89ee..5653647b8 100644 --- a/src/pkg/mail/message_test.go +++ b/src/pkg/mail/message_test.go @@ -94,7 +94,6 @@ func TestDateParsing(t *testing.T) { Hour: 9, Minute: 55, Second: 6, - Weekday: 5, // Fri ZoneOffset: -6 * 60 * 60, }, }, diff --git a/src/pkg/net/cgo_bsd.go b/src/pkg/net/cgo_bsd.go index 4984df4a2..63750f7a3 100644 --- a/src/pkg/net/cgo_bsd.go +++ b/src/pkg/net/cgo_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd + package net /* diff --git a/src/pkg/net/cgo_stub.go b/src/pkg/net/cgo_stub.go index c6277cb65..565cbe7fe 100644 --- a/src/pkg/net/cgo_stub.go +++ b/src/pkg/net/cgo_stub.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build openbsd + // Stub cgo routines for systems that do not use cgo to do network lookups. package net diff --git a/src/pkg/net/cgo_unix.go b/src/pkg/net/cgo_unix.go index a3711d601..ec2a393e8 100644 --- a/src/pkg/net/cgo_unix.go +++ b/src/pkg/net/cgo_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux + package net /* diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go index cb4645509..eb7db5e27 100644 --- a/src/pkg/net/dnsclient_unix.go +++ b/src/pkg/net/dnsclient_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // DNS client: see RFC 1035. // Has to be linked into package net for Dial. @@ -213,6 +215,18 @@ func goLookupHost(name string) (addrs []string, err os.Error) { // depending on our lookup code, so that Go and C get the same // answers. func goLookupIP(name string) (addrs []IP, err os.Error) { + // Use entries from /etc/hosts if possible. + haddrs := lookupStaticHost(name) + if len(haddrs) > 0 { + for _, haddr := range haddrs { + if ip := ParseIP(haddr); ip != nil { + addrs = append(addrs, ip) + } + } + if len(addrs) > 0 { + return + } + } onceLoadConfig.Do(loadConfig) if dnserr != nil || cfg == nil { err = dnserr diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig.go index 54e334342..afc059917 100644 --- a/src/pkg/net/dnsconfig.go +++ b/src/pkg/net/dnsconfig.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Read system DNS config from /etc/resolv.conf package net diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go index 707dccaa4..9084e8875 100644 --- a/src/pkg/net/fd.go +++ b/src/pkg/net/fd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package net import ( diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 3757e143d..b025bddea 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -23,7 +23,7 @@ var initErr os.Error func init() { var d syscall.WSAData - e := syscall.WSAStartup(uint32(0x101), &d) + e := syscall.WSAStartup(uint32(0x202), &d) if e != 0 { initErr = os.NewSyscallError("WSAStartup", e) } @@ -52,15 +52,27 @@ type anOp struct { // of the struct, as our code rely on it. o syscall.Overlapped - resultc chan ioResult // io completion results - errnoc chan int // io submit / cancel operation errors + resultc chan ioResult + errnoc chan int fd *netFD } -func (o *anOp) Init(fd *netFD) { +func (o *anOp) Init(fd *netFD, mode int) { o.fd = fd - o.resultc = make(chan ioResult, 1) - o.errnoc = make(chan int) + var i int + if mode == 'r' { + i = 0 + } else { + i = 1 + } + if fd.resultc[i] == nil { + fd.resultc[i] = make(chan ioResult, 1) + } + o.resultc = fd.resultc[i] + if fd.errnoc[i] == nil { + fd.errnoc[i] = make(chan int) + } + o.errnoc = fd.errnoc[i] } func (o *anOp) Op() *anOp { @@ -74,8 +86,8 @@ type bufOp struct { buf syscall.WSABuf } -func (o *bufOp) Init(fd *netFD, buf []byte) { - o.anOp.Init(fd) +func (o *bufOp) Init(fd *netFD, buf []byte, mode int) { + o.anOp.Init(fd, mode) o.buf.Len = uint32(len(buf)) if len(buf) == 0 { o.buf.Buf = nil @@ -208,12 +220,14 @@ type netFD struct { closing bool // immutable until Close - sysfd syscall.Handle - family int - proto int - net string - laddr Addr - raddr Addr + sysfd syscall.Handle + family int + proto int + net string + laddr Addr + raddr Addr + resultc [2]chan ioResult // read/write completion results + errnoc [2]chan int // read/write submit or cancel operation errors // owned by client rdeadline_delta int64 @@ -325,7 +339,7 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) { return 0, os.EINVAL } var o readOp - o.Init(fd, buf) + o.Init(fd, buf, 'r') n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) if err == nil && n == 0 { err = os.EOF @@ -365,7 +379,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error) return 0, nil, os.EINVAL } var o readFromOp - o.Init(fd, buf) + o.Init(fd, buf, 'r') o.rsan = int32(unsafe.Sizeof(o.rsa)) n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) if err != nil { @@ -402,7 +416,7 @@ func (fd *netFD) Write(buf []byte) (n int, err os.Error) { return 0, os.EINVAL } var o writeOp - o.Init(fd, buf) + o.Init(fd, buf, 'w') return iosrv.ExecIO(&o, fd.wdeadline_delta) } @@ -437,7 +451,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) return 0, os.EINVAL } var o writeToOp - o.Init(fd, buf) + o.Init(fd, buf, 'w') o.sa = sa return iosrv.ExecIO(&o, fd.wdeadline_delta) } @@ -487,7 +501,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. // Submit accept request. var o acceptOp - o.Init(fd) + o.Init(fd, 'r') o.newsock = s _, err = iosrv.ExecIO(&o, 0) if err != nil { diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go index 0e411a192..d8528e41b 100644 --- a/src/pkg/net/file.go +++ b/src/pkg/net/file.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package net import ( diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 2675f94b9..9171827d2 100644 --- a/src/pkg/net/interface_bsd.go +++ b/src/pkg/net/interface_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + // Network interface identification for BSD variants package net diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go index 950de6c59..282b38b5e 100644 --- a/src/pkg/net/interface_stub.go +++ b/src/pkg/net/interface_stub.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build plan9 + // Network interface identification package net diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go index 5cbc58870..35aceb223 100644 --- a/src/pkg/net/iprawsock_posix.go +++ b/src/pkg/net/iprawsock_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + // (Raw) IP sockets package net diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go index 0c522fb7f..049df9ea4 100644 --- a/src/pkg/net/ipsock_posix.go +++ b/src/pkg/net/ipsock_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + package net import ( diff --git a/src/pkg/net/lookup_plan9.go b/src/pkg/net/lookup_plan9.go index 37d6b8e31..ee0c9e879 100644 --- a/src/pkg/net/lookup_plan9.go +++ b/src/pkg/net/lookup_plan9.go @@ -204,6 +204,11 @@ func LookupMX(name string) (mx []*MX, err os.Error) { return } +// LookupTXT returns the DNS TXT records for the given domain name. +func LookupTXT(name string) (txt []string, err os.Error) { + return nil, os.NewError("net.LookupTXT is not implemented on Plan 9") +} + // LookupAddr performs a reverse lookup for the given address, returning a list // of names mapping to that address. func LookupAddr(addr string) (name []string, err os.Error) { diff --git a/src/pkg/net/lookup_test.go b/src/pkg/net/lookup_test.go index 995ab03d0..41066fe48 100644 --- a/src/pkg/net/lookup_test.go +++ b/src/pkg/net/lookup_test.go @@ -42,6 +42,24 @@ func TestGmailMX(t *testing.T) { } } +func TestGmailTXT(t *testing.T) { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + t.Logf("LookupTXT is not implemented on Windows or Plan 9") + return + } + if testing.Short() || avoidMacFirewall { + t.Logf("skipping test to avoid external network") + return + } + txt, err := LookupTXT("gmail.com") + if err != nil { + t.Errorf("failed: %s", err) + } + if len(txt) == 0 || len(txt[0]) == 0 { + t.Errorf("no results") + } +} + func TestGoogleDNSAddr(t *testing.T) { if testing.Short() || avoidMacFirewall { t.Logf("skipping test to avoid external network") diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go index 8f5e66212..7368b751e 100644 --- a/src/pkg/net/lookup_unix.go +++ b/src/pkg/net/lookup_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package net import ( @@ -52,7 +54,7 @@ func LookupCNAME(name string) (cname string, err os.Error) { // LookupSRV tries to resolve an SRV query of the given service, // protocol, and domain name, as specified in RFC 2782. In most cases // the proto argument can be the same as the corresponding -// Addr.Network(). The returned records are sorted by priority +// Addr.Network(). The returned records are sorted by priority // and randomized by weight within a priority. func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { target := "_" + service + "._" + proto + "." + name @@ -72,19 +74,32 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. // LookupMX returns the DNS MX records for the given domain name sorted by preference. func LookupMX(name string) (mx []*MX, err os.Error) { - _, rr, err := lookup(name, dnsTypeMX) + _, records, err := lookup(name, dnsTypeMX) if err != nil { return } - mx = make([]*MX, len(rr)) - for i := range rr { - r := rr[i].(*dnsRR_MX) + mx = make([]*MX, len(records)) + for i, rr := range records { + r := rr.(*dnsRR_MX) mx[i] = &MX{r.Mx, r.Pref} } byPref(mx).sort() return } +// LookupTXT returns the DNS TXT records for the given domain name. +func LookupTXT(name string) (txt []string, err os.Error) { + _, records, err := lookup(name, dnsTypeTXT) + if err != nil { + return + } + txt = make([]string, len(records)) + for i, r := range records { + txt[i] = r.(*dnsRR_TXT).Txt + } + return +} + // LookupAddr performs a reverse lookup for the given address, returning a list // of names mapping to that address. func LookupAddr(addr string) (name []string, err os.Error) { diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go index fa3ad7c7f..b33c7f949 100644 --- a/src/pkg/net/lookup_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -110,6 +110,10 @@ func LookupMX(name string) (mx []*MX, err os.Error) { return mx, nil } +func LookupTXT(name string) (txt []string, err os.Error) { + return nil, os.NewError("net.LookupTXT is not implemented on Windows") +} + func LookupAddr(addr string) (name []string, err os.Error) { arpa, err := reverseaddr(addr) if err != nil { diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver.go index 427208701..3c9a6da53 100644 --- a/src/pkg/net/newpollserver.go +++ b/src/pkg/net/newpollserver.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package net import ( diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go index 8f8327a37..a8ca60c60 100644 --- a/src/pkg/net/port.go +++ b/src/pkg/net/port.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Read system port mappings from /etc/services package net diff --git a/src/pkg/net/sendfile_stub.go b/src/pkg/net/sendfile_stub.go index 43e8104e9..c55be6c08 100644 --- a/src/pkg/net/sendfile_stub.go +++ b/src/pkg/net/sendfile_stub.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + package net import ( diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go index 3772eee24..d9c2f537a 100644 --- a/src/pkg/net/sendfile_windows.go +++ b/src/pkg/net/sendfile_windows.go @@ -54,7 +54,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool) defer c.decref() var o sendfileOp - o.Init(c) + o.Init(c, 'w') o.n = uint32(n) o.src = f.Fd() done, err := iosrv.ExecIO(&o, 0) diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go index 821716e43..366e050ff 100644 --- a/src/pkg/net/sock.go +++ b/src/pkg/net/sock.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + // Sockets package net diff --git a/src/pkg/net/sock_bsd.go b/src/pkg/net/sock_bsd.go index 5fd52074a..c59802fec 100644 --- a/src/pkg/net/sock_bsd.go +++ b/src/pkg/net/sock_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd + // Sockets for BSD variants package net diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go index f2e919702..35d536c31 100644 --- a/src/pkg/net/tcpsock_posix.go +++ b/src/pkg/net/tcpsock_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + // TCP sockets package net diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go index ce0ddc73f..a404f4758 100644 --- a/src/pkg/net/textproto/reader.go +++ b/src/pkg/net/textproto/reader.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "os" "strconv" + "strings" ) // BUG(rsc): To let callers manage exposure to denial of service @@ -182,6 +183,10 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message if err != nil { return } + return parseCodeLine(line, expectCode) +} + +func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err os.Error) { if len(line) < 4 || line[3] != ' ' && line[3] != '-' { err = ProtocolError("short response: " + line) return @@ -224,15 +229,20 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os. return } -// ReadResponse reads a multi-line response of the form +// ReadResponse reads a multi-line response of the form: +// // code-message line 1 // code-message line 2 // ... // code message line n -// where code is a 3-digit status code. Each line should have the same code. -// The response is terminated by a line that uses a space between the code and -// the message line rather than a dash. Each line in message is separated by -// a newline (\n). +// +// where code is a 3-digit status code. The first line starts with the +// code and a hyphen. The response is terminated by a line that starts +// with the same code followed by a space. Each line in message is +// separated by a newline (\n). +// +// See page 36 of RFC 959 (http://www.ietf.org/rfc/rfc959.txt) for +// details. // // If the prefix of the status does not match the digits in expectCode, // ReadResponse returns with err set to &Error{code, message}. @@ -244,11 +254,18 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os. func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) { code, continued, message, err := r.readCodeLine(expectCode) for err == nil && continued { + line, err := r.ReadLine() + if err != nil { + return + } + var code2 int var moreMessage string - code2, continued, moreMessage, err = r.readCodeLine(expectCode) - if code != code2 { - err = ProtocolError("status code mismatch: " + strconv.Itoa(code) + ", " + strconv.Itoa(code2)) + code2, continued, moreMessage, err = parseCodeLine(line, expectCode) + if err != nil || code2 != code { + message += "\n" + strings.TrimRight(line, "\r\n") + continued = true + continue } message += "\n" + moreMessage } diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go index 0658e58b8..23ebc3f61 100644 --- a/src/pkg/net/textproto/reader_test.go +++ b/src/pkg/net/textproto/reader_test.go @@ -138,3 +138,56 @@ func TestReadMIMEHeader(t *testing.T) { t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want) } } + +type readResponseTest struct { + in string + inCode int + wantCode int + wantMsg string +} + +var readResponseTests = []readResponseTest{ + {"230-Anonymous access granted, restrictions apply\n" + + "Read the file README.txt,\n" + + "230 please", + 23, + 230, + "Anonymous access granted, restrictions apply\nRead the file README.txt,\n please", + }, + + {"230 Anonymous access granted, restrictions apply\n", + 23, + 230, + "Anonymous access granted, restrictions apply", + }, + + {"400-A\n400-B\n400 C", + 4, + 400, + "A\nB\nC", + }, + + {"400-A\r\n400-B\r\n400 C\r\n", + 4, + 400, + "A\nB\nC", + }, +} + +// See http://www.ietf.org/rfc/rfc959.txt page 36. +func TestRFC959Lines(t *testing.T) { + for i, tt := range readResponseTests { + r := reader(tt.in + "\nFOLLOWING DATA") + code, msg, err := r.ReadResponse(tt.inCode) + if err != nil { + t.Errorf("#%d: ReadResponse: %v", i, err) + continue + } + if code != tt.wantCode { + t.Errorf("#%d: code=%d, want %d", i, code, tt.wantCode) + } + if msg != tt.wantMsg { + t.Errorf("%#d: msg=%q, want %q", i, msg, tt.wantMsg) + } + } +} diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go index 1dc79f736..06298ee40 100644 --- a/src/pkg/net/udpsock_posix.go +++ b/src/pkg/net/udpsock_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + // UDP sockets package net diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go index 38c6fe9eb..fccf0189c 100644 --- a/src/pkg/net/unixsock_posix.go +++ b/src/pkg/net/unixsock_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + // Unix domain sockets package net diff --git a/src/pkg/os/dir_unix.go b/src/pkg/os/dir_unix.go index 7835ed52b..529593395 100644 --- a/src/pkg/os/dir_unix.go +++ b/src/pkg/os/dir_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package os import ( diff --git a/src/pkg/os/env_unix.go b/src/pkg/os/env_unix.go index 9cc0b03d8..8dd84ae4f 100644 --- a/src/pkg/os/env_unix.go +++ b/src/pkg/os/env_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Unix environment variables. package os diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go index d43f1786d..9dc258a79 100644 --- a/src/pkg/os/error_posix.go +++ b/src/pkg/os/error_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + package os import syscall "syscall" diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index f37bfab58..035b156cb 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + package os import ( diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go index 41e77230a..e1adb203e 100644 --- a/src/pkg/os/exec_unix.go +++ b/src/pkg/os/exec_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package os import ( diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go index 14ddd92c4..526914956 100644 --- a/src/pkg/os/file_posix.go +++ b/src/pkg/os/file_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd windows + package os import ( @@ -46,7 +48,7 @@ func Remove(name string) Error { // both errors will be ENOTDIR, so it's okay to // use the error from unlink. // For windows syscall.ENOTDIR is set - // to syscall.ERROR_DIRECTORY, hopefully it should + // to syscall.ERROR_PATH_NOT_FOUND, hopefully it should // do the trick. if e1 != syscall.ENOTDIR { e = e1 diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go index ab32ce98d..a4470f1b4 100644 --- a/src/pkg/os/file_unix.go +++ b/src/pkg/os/file_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package os import ( diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index 0cdd2fdf6..a3f5b4459 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -92,15 +92,6 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err Error) { if e == nil { return r, nil } - // Imitating Unix behavior by replacing syscall.ERROR_PATH_NOT_FOUND with - // os.ENOTDIR. Not sure if we should go into that. - if e2, ok := e.(*PathError); ok { - if e3, ok := e2.Error.(Errno); ok { - if e3 == Errno(syscall.ERROR_PATH_NOT_FOUND) { - return nil, &PathError{"open", name, ENOTDIR} - } - } - } return nil, e } diff --git a/src/pkg/os/mkunixsignals.sh b/src/pkg/os/mkunixsignals.sh index 4bbc43f3d..df3236210 100755 --- a/src/pkg/os/mkunixsignals.sh +++ b/src/pkg/os/mkunixsignals.sh @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -echo '// ./mkunix.sh' "$1" +echo '// ./mkunixsignals.sh' "$1" echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT' echo diff --git a/src/pkg/os/path.go b/src/pkg/os/path.go index a8dfce307..b190c51e6 100644 --- a/src/pkg/os/path.go +++ b/src/pkg/os/path.go @@ -68,7 +68,7 @@ func RemoveAll(path string) Error { // Otherwise, is this a directory we need to recurse into? dir, serr := Lstat(path) if serr != nil { - if serr, ok := serr.(*PathError); ok && serr.Error == ENOENT { + if serr, ok := serr.(*PathError); ok && (serr.Error == ENOENT || serr.Error == ENOTDIR) { return nil } return serr diff --git a/src/pkg/os/path_unix.go b/src/pkg/os/path_unix.go index 0d327cddd..33045b60c 100644 --- a/src/pkg/os/path_unix.go +++ b/src/pkg/os/path_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package os const ( diff --git a/src/pkg/os/str.go b/src/pkg/os/str.go index 8dc9e4747..e3606b61e 100644 --- a/src/pkg/os/str.go +++ b/src/pkg/os/str.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build plan9 + package os func itoa(val int) string { // do it here rather than with fmt to avoid dependency diff --git a/src/pkg/os/sys_bsd.go b/src/pkg/os/sys_bsd.go index 188993b69..b0d097a22 100644 --- a/src/pkg/os/sys_bsd.go +++ b/src/pkg/os/sys_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + // os code shared between *BSD systems including OS X (Darwin) // and FreeBSD. diff --git a/src/pkg/os/user/lookup_stubs.go b/src/pkg/os/user/lookup_stubs.go index 2f08f70fd..2d2de989f 100644 --- a/src/pkg/os/user/lookup_stubs.go +++ b/src/pkg/os/user/lookup_stubs.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build openbsd plan9 windows + package user import ( diff --git a/src/pkg/os/user/lookup_unix.go b/src/pkg/os/user/lookup_unix.go index 1b2c9e8c9..817eb791c 100644 --- a/src/pkg/os/user/lookup_unix.go +++ b/src/pkg/os/user/lookup_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux + package user import ( diff --git a/src/pkg/path/Makefile b/src/pkg/path/Makefile index fc3e2519c..a7e05714a 100644 --- a/src/pkg/path/Makefile +++ b/src/pkg/path/Makefile @@ -9,6 +9,4 @@ GOFILES=\ match.go\ path.go\ -GOFILES+=$(GOFILES_$(GOOS)) - include ../../Make.pkg diff --git a/src/pkg/path/filepath/match.go b/src/pkg/path/filepath/match.go index 7fcc214c0..0ccc87e65 100644 --- a/src/pkg/path/filepath/match.go +++ b/src/pkg/path/filepath/match.go @@ -215,7 +215,7 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) { func Glob(pattern string) (matches []string, err os.Error) { if !hasMeta(pattern) { if _, err = os.Stat(pattern); err != nil { - return + return nil, nil } return []string{pattern}, nil } diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go index a1c8333f3..711e835fb 100644 --- a/src/pkg/path/filepath/match_test.go +++ b/src/pkg/path/filepath/match_test.go @@ -124,6 +124,16 @@ func TestGlob(t *testing.T) { t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result) } } + for _, pattern := range []string{"no_match", "../*/no_match"} { + matches, err := Glob(pattern) + if err != nil { + t.Errorf("Glob error for %q: %s", pattern, err) + continue + } + if len(matches) != 0 { + t.Errorf("Glob(%#q) = %#v want []", pattern, matches) + } + } } func TestGlobError(t *testing.T) { diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index 3d5b915c1..c40d9ff56 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -41,9 +41,12 @@ func Clean(path string) string { vol := VolumeName(path) path = path[len(vol):] if path == "" { + if len(vol) > 1 && vol[1] != ':' { + // should be UNC + return FromSlash(vol) + } return vol + "." } - rooted := os.IsPathSeparator(path[0]) // Invariants: @@ -144,8 +147,9 @@ func SplitList(path string) []string { // If there is no Separator in path, Split returns an empty dir // and file set to path. func Split(path string) (dir, file string) { + vol := VolumeName(path) i := len(path) - 1 - for i >= 0 && !os.IsPathSeparator(path[i]) { + for i >= len(vol) && !os.IsPathSeparator(path[i]) { i-- } return path[:i+1], path[i+1:] @@ -254,38 +258,63 @@ func Abs(path string) (string, os.Error) { return Join(wd, path), nil } -// Visitor methods are invoked for corresponding file tree entries -// visited by Walk. The parameter path is the full path of f relative -// to root. -type Visitor interface { - VisitDir(path string, f *os.FileInfo) bool - VisitFile(path string, f *os.FileInfo) -} +// SkipDir is used as a return value from WalkFuncs to indicate that +// the directory named in the call is to be skipped. It is not returned +// as an error by any function. +var SkipDir = os.NewError("skip this directory") -func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) { - if !f.IsDirectory() { - v.VisitFile(path, f) - return +// WalkFunc is the type of the function called for each file or directory +// visited by Walk. If there was a problem walking to the file or directory +// named by path, the incoming error will describe the problem and the +// function can decide how to handle that error (and Walk will not descend +// into that directory). If an error is returned, processing stops. The +// sole exception is that if path is a directory and the function returns the +// special value SkipDir, the contents of the directory are skipped +// and processing continues as usual on the next file. +type WalkFunc func(path string, info *os.FileInfo, err os.Error) os.Error + +// walk recursively descends path, calling w. +func walk(path string, info *os.FileInfo, walkFn WalkFunc) os.Error { + err := walkFn(path, info, nil) + if err != nil { + if info.IsDirectory() && err == SkipDir { + return nil + } + return err } - if !v.VisitDir(path, f) { - return // skip directory entries + if !info.IsDirectory() { + return nil } list, err := readDir(path) if err != nil { - if errors != nil { - errors <- err + return walkFn(path, info, err) + } + + for _, fileInfo := range list { + if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil { + return err } } + return nil +} - for _, e := range list { - walk(Join(path, e.Name), e, v, errors) +// Walk walks the file tree rooted at root, calling walkFn for each file or +// directory in the tree, including root. All errors that arise visiting files +// and directories are filtered by walkFn. The files are walked in lexical +// order, which makes the output deterministic but means that for very +// large directories Walk can be inefficient. +func Walk(root string, walkFn WalkFunc) os.Error { + info, err := os.Lstat(root) + if err != nil { + return walkFn(root, nil, err) } + return walk(root, info, walkFn) } // readDir reads the directory named by dirname and returns -// a list of sorted directory entries. +// a sorted list of directory entries. // Copied from io/ioutil to avoid the circular import. func readDir(dirname string) ([]*os.FileInfo, os.Error) { f, err := os.Open(dirname) @@ -312,24 +341,6 @@ func (f fileInfoList) Len() int { return len(f) } func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -// Walk walks the file tree rooted at root, calling v.VisitDir or -// v.VisitFile for each directory or file in the tree, including root. -// If v.VisitDir returns false, Walk skips the directory's entries; -// otherwise it invokes itself for each directory entry in sorted order. -// An error reading a directory does not abort the Walk. -// If errors != nil, Walk sends each directory read error -// to the channel. Otherwise Walk discards the error. -func Walk(root string, v Visitor, errors chan<- os.Error) { - f, err := os.Lstat(root) - if err != nil { - if errors != nil { - errors <- err - } - return // can't progress - } - walk(root, f, v, errors) -} - // Base returns the last element of path. // Trailing path separators are removed before extracting the last element. // If the path is empty, Base returns ".". diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index 395b12775..850ead8e8 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -73,9 +73,17 @@ var wincleantests = []PathTest{ {`c:\abc`, `c:\abc`}, {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, {`c:\abc\def\..\..`, `c:\`}, + {`c:\..\abc`, `c:\abc`}, {`c:..\abc`, `c:..\abc`}, {`\`, `\`}, {`/`, `\`}, + {`\\i\..\c$`, `\c$`}, + {`\\i\..\i\c$`, `\i\c$`}, + {`\\i\..\I\c$`, `\I\c$`}, + {`\\host\share\foo\..\bar`, `\\host\share\bar`}, + {`//host/share/foo/../baz`, `\\host\share\baz`}, + {`\\a\b\..\c`, `\\a\b\c`}, + {`\\a\b`, `\\a\b`}, } func TestClean(t *testing.T) { @@ -146,9 +154,25 @@ var unixsplittests = []SplitTest{ {"/", "/", ""}, } +var winsplittests = []SplitTest{ + {`c:`, `c:`, ``}, + {`c:/`, `c:/`, ``}, + {`c:/foo`, `c:/`, `foo`}, + {`c:/foo/bar`, `c:/foo/`, `bar`}, + {`//host/share`, `//host/share`, ``}, + {`//host/share/`, `//host/share/`, ``}, + {`//host/share/foo`, `//host/share/`, `foo`}, + {`\\host\share`, `\\host\share`, ``}, + {`\\host\share\`, `\\host\share\`, ``}, + {`\\host\share\foo`, `\\host\share\`, `foo`}, +} + func TestSplit(t *testing.T) { var splittests []SplitTest splittests = unixsplittests + if runtime.GOOS == "windows" { + splittests = append(splittests, winsplittests...) + } for _, test := range splittests { if d, f := filepath.Split(test.path); d != test.dir || f != test.file { t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) @@ -186,6 +210,8 @@ var winjointests = []JoinTest{ {[]string{`C:\Windows\`, ``}, `C:\Windows`}, {[]string{`C:\`, `Windows`}, `C:\Windows`}, {[]string{`C:`, `Windows`}, `C:\Windows`}, + {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, + {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, } // join takes a []string and passes it to Join. @@ -280,9 +306,9 @@ func makeTree(t *testing.T) { func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } -func checkMarks(t *testing.T) { +func checkMarks(t *testing.T, report bool) { walkTree(tree, tree.name, func(path string, n *Node) { - if n.mark != 1 { + if n.mark != 1 && report { t.Errorf("node %s mark = %d; expected 1", path, n.mark) } n.mark = 0 @@ -290,44 +316,41 @@ func checkMarks(t *testing.T) { } // Assumes that each node name is unique. Good enough for a test. -func mark(name string) { - name = filepath.ToSlash(name) +// If clear is true, any incoming error is cleared before return. The errors +// are always accumulated, though. +func mark(path string, info *os.FileInfo, err os.Error, errors *[]os.Error, clear bool) os.Error { + if err != nil { + *errors = append(*errors, err) + if clear { + return nil + } + return err + } walkTree(tree, tree.name, func(path string, n *Node) { - if n.name == name { + if n.name == info.Name { n.mark++ } }) -} - -type TestVisitor struct{} - -func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool { - mark(f.Name) - return true -} - -func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) { - mark(f.Name) + return nil } func TestWalk(t *testing.T) { makeTree(t) - - // 1) ignore error handling, expect none - v := &TestVisitor{} - filepath.Walk(tree.name, v, nil) - checkMarks(t) - - // 2) handle errors, expect none - errors := make(chan os.Error, 64) - filepath.Walk(tree.name, v, errors) - select { - case err := <-errors: + errors := make([]os.Error, 0, 10) + clear := true + markFn := func(path string, info *os.FileInfo, err os.Error) os.Error { + return mark(path, info, err, &errors, clear) + } + // Expect no errors. + err := filepath.Walk(tree.name, markFn) + if err != nil { t.Errorf("no error expected, found: %s", err) - default: - // ok } - checkMarks(t) + if len(errors) != 0 { + t.Errorf("unexpected errors: %s", errors) + } + checkMarks(t, true) + errors = errors[0:0] // Test permission errors. Only possible if we're not root // and only on some file systems (AFS, FAT). To avoid errors during @@ -336,40 +359,50 @@ func TestWalk(t *testing.T) { // introduce 2 errors: chmod top-level directories to 0 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) + + // 3) capture errors, expect two. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- + err := filepath.Walk(tree.name, markFn) + if err != nil { + t.Errorf("expected no error return from Walk, %s", err) + } + if len(errors) != 2 { + t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) + } + // the inaccessible subtrees were marked manually + checkMarks(t, true) + errors = errors[0:0] - // 3) handle errors, expect two - errors = make(chan os.Error, 64) - os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) - filepath.Walk(tree.name, v, errors) - Loop: - for i := 1; i <= 2; i++ { - select { - case <-errors: - // ok - default: - t.Errorf("%d. error expected, none found", i) - break Loop - } + // 4) capture errors, stop after first error. + // mark respective subtrees manually + markTree(tree.entries[1]) + markTree(tree.entries[3]) + // correct double-marking of directory itself + tree.entries[1].mark-- + tree.entries[3].mark-- + clear = false // error will stop processing + err = filepath.Walk(tree.name, markFn) + if err == nil { + t.Errorf("expected error return from Walk") } - select { - case err := <-errors: - t.Errorf("only two errors expected, found 3rd: %v", err) - default: - // ok + if len(errors) != 1 { + t.Errorf("expected 1 error, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually - checkMarks(t) + checkMarks(t, false) + errors = errors[0:0] + + // restore permissions + os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) + os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) } // cleanup - os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) - os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) if err := os.RemoveAll(tree.name); err != nil { t.Errorf("removeTree: %v", err) } @@ -422,6 +455,8 @@ var winisabstests = []IsAbsTest{ {`\`, false}, {`\Windows`, false}, {`c:a\b`, false}, + {`\\host\share\foo`, true}, + {`//host/share/foo/bar`, true}, } func TestIsAbs(t *testing.T) { @@ -574,3 +609,43 @@ func TestAbs(t *testing.T) { } } } + +type VolumeNameTest struct { + path string + vol string +} + +var volumenametests = []VolumeNameTest{ + {`c:/foo/bar`, `c:`}, + {`c:`, `c:`}, + {``, ``}, + {`\\\host`, ``}, + {`\\\host\`, ``}, + {`\\\host\share`, ``}, + {`\\\host\\share`, ``}, + {`\\host`, ``}, + {`//host`, ``}, + {`\\host\`, ``}, + {`//host/`, ``}, + {`\\host\share`, `\\host\share`}, + {`//host/share`, `//host/share`}, + {`\\host\share\`, `\\host\share`}, + {`//host/share/`, `//host/share`}, + {`\\host\share\foo`, `\\host\share`}, + {`//host/share/foo`, `//host/share`}, + {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, + {`//host/share//foo///bar////baz`, `//host/share`}, + {`\\host\share\foo\..\bar`, `\\host\share`}, + {`//host/share/foo/../bar`, `//host/share`}, +} + +func TestVolumeName(t *testing.T) { + if runtime.GOOS != "windows" { + return + } + for _, v := range volumenametests { + if vol := filepath.VolumeName(v.path); vol != v.vol { + t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol) + } + } +} diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go index b2a4151c1..daf0eb2af 100644 --- a/src/pkg/path/filepath/path_unix.go +++ b/src/pkg/path/filepath/path_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package filepath import "strings" diff --git a/src/pkg/path/filepath/path_windows.go b/src/pkg/path/filepath/path_windows.go index 2535697fd..9692fd978 100644 --- a/src/pkg/path/filepath/path_windows.go +++ b/src/pkg/path/filepath/path_windows.go @@ -4,7 +4,13 @@ package filepath -import "strings" +import ( + "strings" +) + +func isSlash(c uint8) bool { + return c == '\\' || c == '/' +} // IsAbs returns true if the path is absolute. func IsAbs(path string) (b bool) { @@ -16,11 +22,12 @@ func IsAbs(path string) (b bool) { if path == "" { return false } - return path[0] == '/' || path[0] == '\\' + return isSlash(path[0]) } // VolumeName returns leading volume name. // Given "C:\foo\bar" it returns "C:" under windows. +// Given "\\host\share\foo" it returns "\\host\share". // On other platforms it returns "". func VolumeName(path string) (v string) { if len(path) < 2 { @@ -33,6 +40,30 @@ func VolumeName(path string) (v string) { 'A' <= c && c <= 'Z') { return path[:2] } + // is it UNC + if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && + !isSlash(path[2]) && path[2] != '.' { + // first, leading `\\` and next shouldn't be `\`. its server name. + for n := 3; n < l-1; n++ { + // second, next '\' shouldn't be repeated. + if isSlash(path[n]) { + n++ + // third, following something characters. its share name. + if !isSlash(path[n]) { + if path[n] == '.' { + break + } + for ; n < l; n++ { + if isSlash(path[n]) { + break + } + } + return path[:n] + } + break + } + } + } return "" } diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index c9a9edc73..3630069bb 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -10,6 +10,9 @@ // A call to ValueOf returns a Value representing the run-time data. // Zero takes a Type and returns a Value representing a zero value // for that type. +// +// See "The Laws of Reflection" for an introduction to reflection in Go: +// http://blog.golang.org/2011/09/laws-of-reflection.html package reflect import ( @@ -713,7 +716,7 @@ type StructTag string // Get returns the value associated with key in the tag string. // If there is no such key in the tag, Get returns the empty string. // If the tag does not have the conventional format, the value -// returned by Get is unspecified, +// returned by Get is unspecified. func (tag StructTag) Get(key string) string { for tag != "" { // skip leading space diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 8e5a6282c..1cace3fda 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -1444,6 +1444,8 @@ func (v Value) String() string { case String: return *(*string)(iv.addr) } + // If you call String on a reflect.Value of other type, it's better to + // print something than to panic. Useful in debugging. return "<" + iv.typ.String() + " Value>" } diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index e79317bf6..03d6f7d62 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -603,8 +603,6 @@ runtime·gc(int32 force) m->gcing = 1; runtime·stoptheworld(); - if(runtime·mheap.Lock.key != 0) - runtime·throw("runtime·mheap locked during gc"); cachestats(); heap0 = mstats.heap_alloc; diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c index 37d505681..7d24a6540 100644 --- a/src/pkg/runtime/mheap.c +++ b/src/pkg/runtime/mheap.c @@ -101,6 +101,7 @@ HaveSpan: runtime·throw("MHeap_AllocLocked - bad npages"); runtime·MSpanList_Remove(s); s->state = MSpanInUse; + mstats.heap_idle -= s->npages<<PageShift; if(s->npages > npage) { // Trim extra and put it back in the heap. @@ -276,6 +277,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<<PageShift, s->state, s->ref); runtime·throw("MHeap_FreeLocked - invalid free"); } + mstats.heap_idle += s->npages<<PageShift; s->state = MSpanFree; runtime·MSpanList_Remove(s); sp = (uintptr*)(s->start<<PageShift); diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go index 4486d5525..5f128c01c 100644 --- a/src/pkg/runtime/pprof/pprof_test.go +++ b/src/pkg/runtime/pprof/pprof_test.go @@ -22,9 +22,6 @@ func TestCPUProfile(t *testing.T) { case "plan9": // unimplemented return - case "windows": - // unimplemented - return } buf := make([]byte, 100000) diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 6feedcbc8..999511ac2 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -119,10 +119,10 @@ enum */ struct Lock { - uint32 key; #ifdef __WINDOWS__ - void* event; + M* waitm; // linked list of waiting M's #else + uint32 key; uint32 sema; // for OS X #endif }; @@ -212,6 +212,7 @@ struct G uintptr sigcode1; uintptr sigpc; uintptr gopc; // pc of go statement that created this goroutine + uintptr end[]; }; struct M { @@ -251,6 +252,13 @@ struct M uint32 freglo[16]; // D[i] lsb and F[i] uint32 freghi[16]; // D[i] msb and F[i+16] uint32 fflag; // floating point compare flags + +#ifdef __WINDOWS__ + void* thread; // thread handle + void* event; // event for signalling + M* nextwaitm; // next M waiting for lock +#endif + uintptr end[]; }; struct Stktop @@ -307,7 +315,8 @@ struct WinCall void (*fn)(void*); uintptr n; // number of parameters void* args; // parameters - uintptr r; // return value + uintptr r1; // return values + uintptr r2; uintptr err; // error number }; diff --git a/src/pkg/runtime/stack.h b/src/pkg/runtime/stack.h index 44d5533f4..483233876 100644 --- a/src/pkg/runtime/stack.h +++ b/src/pkg/runtime/stack.h @@ -58,7 +58,7 @@ enum { // purposes like signal handling. Used on Windows because // it does not use a separate stack. #ifdef __WINDOWS__ - StackSystem = 2048, + StackSystem = 512 * sizeof(uintptr), #else StackSystem = 0, #endif diff --git a/src/pkg/runtime/syscall_windows_test.go b/src/pkg/runtime/syscall_windows_test.go index c27060701..32eb0533f 100644 --- a/src/pkg/runtime/syscall_windows_test.go +++ b/src/pkg/runtime/syscall_windows_test.go @@ -39,6 +39,77 @@ func TestStdCall(t *testing.T) { } } +func Test64BitReturnStdCall(t *testing.T) { + + const ( + VER_BUILDNUMBER = 0x0000004 + VER_MAJORVERSION = 0x0000002 + VER_MINORVERSION = 0x0000001 + VER_PLATFORMID = 0x0000008 + VER_PRODUCT_TYPE = 0x0000080 + VER_SERVICEPACKMAJOR = 0x0000020 + VER_SERVICEPACKMINOR = 0x0000010 + VER_SUITENAME = 0x0000040 + + VER_EQUAL = 1 + VER_GREATER = 2 + VER_GREATER_EQUAL = 3 + VER_LESS = 4 + VER_LESS_EQUAL = 5 + + ERROR_OLD_WIN_VERSION = 1150 + ) + + type OSVersionInfoEx struct { + OSVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformId uint32 + CSDVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + Reserve byte + } + + kernel32, e := syscall.LoadLibrary("kernel32.dll") + if e != 0 { + t.Fatalf("LoadLibrary(kernel32.dll) failed: %s", syscall.Errstr(e)) + } + setMask, e := syscall.GetProcAddress(kernel32, "VerSetConditionMask") + if e != 0 { + t.Fatalf("GetProcAddress(kernel32.dll, VerSetConditionMask) failed: %s", syscall.Errstr(e)) + } + verifyVersion, e := syscall.GetProcAddress(kernel32, "VerifyVersionInfoW") + if e != 0 { + t.Fatalf("GetProcAddress(kernel32.dll, VerifyVersionInfoW) failed: %s", syscall.Errstr(e)) + } + + var m1, m2 uintptr + m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL, 0, 0) + m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL, 0, 0) + m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL, 0, 0) + m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL, 0, 0) + + vi := OSVersionInfoEx{ + MajorVersion: 5, + MinorVersion: 1, + ServicePackMajor: 2, + ServicePackMinor: 0, + } + vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi)) + r, _, e2 := syscall.Syscall6(verifyVersion, + 4, + uintptr(unsafe.Pointer(&vi)), + VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR, + m1, m2, 0, 0) + if r == 0 && e2 != ERROR_OLD_WIN_VERSION { + t.Errorf("VerifyVersionInfo failed: (%d) %s", e2, syscall.Errstr(int(e2))) + } +} + func TestCDecl(t *testing.T) { h, e := syscall.LoadLibrary("user32.dll") if e != 0 { diff --git a/src/pkg/runtime/windows/386/defs.h b/src/pkg/runtime/windows/386/defs.h index 49fc19504..6cc5336a9 100644 --- a/src/pkg/runtime/windows/386/defs.h +++ b/src/pkg/runtime/windows/386/defs.h @@ -10,9 +10,13 @@ enum { PROT_EXEC = 0x4, MAP_ANON = 0x1, MAP_PRIVATE = 0x2, + DUPLICATE_SAME_ACCESS = 0x2, + THREAD_PRIORITY_HIGHEST = 0x2, SIGINT = 0x2, CTRL_C_EVENT = 0, CTRL_BREAK_EVENT = 0x1, + CONTEXT_CONTROL = 0x10001, + CONTEXT_FULL = 0x10007, EXCEPTION_ACCESS_VIOLATION = 0xc0000005, EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, diff --git a/src/pkg/runtime/windows/386/signal.c b/src/pkg/runtime/windows/386/signal.c index cc6a2302f..9c912ede4 100644 --- a/src/pkg/runtime/windows/386/signal.c +++ b/src/pkg/runtime/windows/386/signal.c @@ -90,9 +90,7 @@ runtime·sighandler(ExceptionRecord *info, void *frame, Context *r) } void -runtime·resetcpuprofiler(int32 hz) +runtime·dosigprof(Context *r, G *gp) { - // TODO: Enable profiling interrupts. - - m->profilehz = hz; + runtime·sigprof((uint8*)r->Eip, (uint8*)r->Esp, nil, gp); } diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s index 94aed83f0..95ae5336b 100644 --- a/src/pkg/runtime/windows/386/sys.s +++ b/src/pkg/runtime/windows/386/sys.s @@ -6,35 +6,35 @@ // void runtime·asmstdcall(void *c); TEXT runtime·asmstdcall(SB),7,$0 - MOVL c+0(FP), DX + MOVL c+0(FP), BX // SetLastError(0). MOVL $0, 0x34(FS) // Copy args to the stack. MOVL SP, BP - MOVL wincall_n(DX), CX // words - MOVL CX, BX - SALL $2, BX - SUBL BX, SP // room for args + MOVL wincall_n(BX), CX // words + MOVL CX, AX + SALL $2, AX + SUBL AX, SP // room for args MOVL SP, DI - MOVL wincall_args(DX), SI + MOVL wincall_args(BX), SI CLD REP; MOVSL // Call stdcall or cdecl function. // DI SI BP BX are preserved, SP is not - MOVL wincall_fn(DX), AX - CALL AX + CALL wincall_fn(BX) MOVL BP, SP // Return result. - MOVL c+0(FP), DX - MOVL AX, wincall_r(DX) + MOVL c+0(FP), BX + MOVL AX, wincall_r1(BX) + MOVL DX, wincall_r2(BX) // GetLastError(). - MOVL 0x34(FS), BX - MOVL BX, wincall_err(DX) + MOVL 0x34(FS), AX + MOVL AX, wincall_err(BX) RET @@ -96,31 +96,52 @@ TEXT runtime·sigtramp1(SB),0,$16-40 sigdone: RET -// Windows runs the ctrl handler in a new thread. TEXT runtime·ctrlhandler(SB),7,$0 + PUSHL $runtime·ctrlhandler1(SB) + CALL runtime·externalthreadhandler(SB) + MOVL 4(SP), CX + ADDL $12, SP + JMP CX + +TEXT runtime·profileloop(SB),7,$0 + PUSHL $runtime·profileloop1(SB) + CALL runtime·externalthreadhandler(SB) + MOVL 4(SP), CX + ADDL $12, SP + JMP CX + +TEXT runtime·externalthreadhandler(SB),7,$0 PUSHL BP MOVL SP, BP PUSHL BX PUSHL SI PUSHL DI PUSHL 0x2c(FS) - MOVL SP, BX + MOVL SP, DX // setup dummy m, g - SUBL $(m_fflag+4), SP // at least space for m_fflag + SUBL $m_end, SP // space for M + MOVL SP, 0(SP) + MOVL $m_end, 4(SP) + CALL runtime·memclr(SB) // smashes AX,BX,CX + LEAL m_tls(SP), CX MOVL CX, 0x2c(FS) MOVL SP, m(CX) - MOVL SP, DX - SUBL $8, SP // space for g_stack{guard,base} + MOVL SP, BX + SUBL $g_end, SP // space for G MOVL SP, g(CX) - MOVL SP, m_g0(DX) + MOVL SP, m_g0(BX) + + MOVL SP, 0(SP) + MOVL $g_end, 4(SP) + CALL runtime·memclr(SB) // smashes AX,BX,CX LEAL -4096(SP), CX MOVL CX, g_stackguard(SP) - MOVL BX, g_stackbase(SP) + MOVL DX, g_stackbase(SP) - PUSHL 8(BP) - CALL runtime·ctrlhandler1(SB) + PUSHL 16(BP) // arg for handler + CALL 8(BP) POPL CX get_tls(CX) @@ -131,9 +152,7 @@ TEXT runtime·ctrlhandler(SB),7,$0 POPL SI POPL BX POPL BP - MOVL 0(SP), CX - ADDL $8, SP - JMP CX + RET // Called from dynamic function created by ../thread.c compilecallback, // running on Windows stack (not Go stack). diff --git a/src/pkg/runtime/windows/amd64/defs.h b/src/pkg/runtime/windows/amd64/defs.h index 30c66df51..d5191a3d7 100644 --- a/src/pkg/runtime/windows/amd64/defs.h +++ b/src/pkg/runtime/windows/amd64/defs.h @@ -10,9 +10,13 @@ enum { PROT_EXEC = 0x4, MAP_ANON = 0x1, MAP_PRIVATE = 0x2, + DUPLICATE_SAME_ACCESS = 0x2, + THREAD_PRIORITY_HIGHEST = 0x2, SIGINT = 0x2, CTRL_C_EVENT = 0, CTRL_BREAK_EVENT = 0x1, + CONTEXT_CONTROL = 0x100001, + CONTEXT_FULL = 0x10000b, EXCEPTION_ACCESS_VIOLATION = 0xc0000005, EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, diff --git a/src/pkg/runtime/windows/amd64/signal.c b/src/pkg/runtime/windows/amd64/signal.c index 1e621b760..97106c8b8 100644 --- a/src/pkg/runtime/windows/amd64/signal.c +++ b/src/pkg/runtime/windows/amd64/signal.c @@ -100,9 +100,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) } void -runtime·resetcpuprofiler(int32 hz) +runtime·dosigprof(Context *r, G *gp) { - // TODO: Enable profiling interrupts. - - m->profilehz = hz; + runtime·sigprof((uint8*)r->Rip, (uint8*)r->Rsp, nil, gp); } diff --git a/src/pkg/runtime/windows/amd64/sys.s b/src/pkg/runtime/windows/amd64/sys.s index 9b4a17eda..113db2004 100644 --- a/src/pkg/runtime/windows/amd64/sys.s +++ b/src/pkg/runtime/windows/amd64/sys.s @@ -49,7 +49,7 @@ loadregs: // Return result. POPQ CX - MOVQ AX, wincall_r(CX) + MOVQ AX, wincall_r1(CX) // GetLastError(). MOVQ 0x30(GS), DI @@ -100,31 +100,51 @@ TEXT runtime·sigtramp(SB),7,$56 sigdone: RET -// Windows runs the ctrl handler in a new thread. -TEXT runtime·ctrlhandler(SB),7,$0 +TEXT runtime·ctrlhandler(SB),7,$8 + MOVQ CX, 16(SP) // spill + MOVQ $runtime·ctrlhandler1(SB), CX + MOVQ CX, 0(SP) + CALL runtime·externalthreadhandler(SB) + RET + +TEXT runtime·profileloop(SB),7,$8 + MOVQ $runtime·profileloop1(SB), CX + MOVQ CX, 0(SP) + CALL runtime·externalthreadhandler(SB) + RET + +TEXT runtime·externalthreadhandler(SB),7,$0 PUSHQ BP MOVQ SP, BP PUSHQ BX PUSHQ SI PUSHQ DI PUSHQ 0x58(GS) - MOVQ SP, BX + MOVQ SP, DX // setup dummy m, g - SUBQ $(m_fflag+4), SP // at least space for m_fflag + SUBQ $m_end, SP // space for M + MOVQ SP, 0(SP) + MOVQ $m_end, 8(SP) + CALL runtime·memclr(SB) // smashes AX,BX,CX + LEAQ m_tls(SP), CX MOVQ CX, 0x58(GS) MOVQ SP, m(CX) - MOVQ SP, DX - SUBQ $16, SP // space for g_stack{guard,base} + MOVQ SP, BX + SUBQ $g_end, SP // space for G MOVQ SP, g(CX) - MOVQ SP, m_g0(DX) + MOVQ SP, m_g0(BX) + + MOVQ SP, 0(SP) + MOVQ $g_end, 8(SP) + CALL runtime·memclr(SB) // smashes AX,BX,CX LEAQ -8192(SP), CX MOVQ CX, g_stackguard(SP) - MOVQ BX, g_stackbase(SP) + MOVQ DX, g_stackbase(SP) - PUSHQ 16(BP) - CALL runtime·ctrlhandler1(SB) + PUSHQ 32(BP) // arg for handler + CALL 16(BP) POPQ CX get_tls(CX) diff --git a/src/pkg/runtime/windows/defs.c b/src/pkg/runtime/windows/defs.c index b076afd5d..1b07dfbc1 100644 --- a/src/pkg/runtime/windows/defs.c +++ b/src/pkg/runtime/windows/defs.c @@ -17,10 +17,16 @@ enum { $MAP_ANON = 1, $MAP_PRIVATE = 2, + $DUPLICATE_SAME_ACCESS = DUPLICATE_SAME_ACCESS, + $THREAD_PRIORITY_HIGHEST = THREAD_PRIORITY_HIGHEST, + $SIGINT = SIGINT, $CTRL_C_EVENT = CTRL_C_EVENT, $CTRL_BREAK_EVENT = CTRL_BREAK_EVENT, + $CONTEXT_CONTROL = CONTEXT_CONTROL, + $CONTEXT_FULL = CONTEXT_FULL, + $EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION, $EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT, $EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND, diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h index a8cc299b8..21277c64b 100644 --- a/src/pkg/runtime/windows/os.h +++ b/src/pkg/runtime/windows/os.h @@ -12,10 +12,9 @@ extern void *runtime·GetProcAddress; #pragma varargck type runtime·stdcall uintptr void runtime·asmstdcall(void *c); void *runtime·stdcall(void *fn, int32 count, ...); -uintptr runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err); -uintptr runtime·getlasterror(void); -void runtime·setlasterror(uintptr err); +uint32 runtime·getlasterror(void); +void runtime·setlasterror(uint32 err); // Function to be called by windows CreateThread // to start new os thread. diff --git a/src/pkg/runtime/windows/syscall.goc b/src/pkg/runtime/windows/syscall.goc index 4777a6189..68c3a4dfa 100644 --- a/src/pkg/runtime/windows/syscall.goc +++ b/src/pkg/runtime/windows/syscall.goc @@ -5,15 +5,28 @@ package syscall #include "runtime.h" #include "os.h" +#include "cgocall.h" func loadlibraryex(filename uintptr) (handle uintptr) { uintptr args[3] = { filename }; - handle = runtime·syscall(runtime·LoadLibraryEx, 3, args, nil); + WinCall c; + + c.fn = runtime·LoadLibraryEx; + c.n = 3; + c.args = &args[0]; + runtime·cgocall(runtime·asmstdcall, &c); + handle = c.r1; } func getprocaddress(handle uintptr, procname uintptr) (proc uintptr) { + WinCall c; + USED(procname); - proc = runtime·syscall(runtime·GetProcAddress, 2, &handle, nil); + c.fn = runtime·GetProcAddress; + c.n = 2; + c.args = &handle; + runtime·cgocall(runtime·asmstdcall, &c); + proc = c.r1; } func NewCallback(fn Eface) (code uintptr) { @@ -25,23 +38,39 @@ func NewCallbackCDecl(fn Eface) (code uintptr) { } func Syscall(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) { + WinCall c; + USED(a2); USED(a3); - r1 = runtime·syscall((void*)fn, nargs, &a1, &err); - r2 = 0; + c.fn = (void*)fn; + c.n = nargs; + c.args = &a1; + runtime·cgocall(runtime·asmstdcall, &c); + err = c.err; + r1 = c.r1; + r2 = c.r2; } func Syscall6(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr) (r1 uintptr, r2 uintptr, err uintptr) { + WinCall c; + USED(a2); USED(a3); USED(a4); USED(a5); USED(a6); - r1 = runtime·syscall((void*)fn, nargs, &a1, &err); - r2 = 0; + c.fn = (void*)fn; + c.n = nargs; + c.args = &a1; + runtime·cgocall(runtime·asmstdcall, &c); + err = c.err; + r1 = c.r1; + r2 = c.r2; } func Syscall9(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr) (r1 uintptr, r2 uintptr, err uintptr) { + WinCall c; + USED(a2); USED(a3); USED(a4); @@ -50,11 +79,18 @@ func Syscall9(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 USED(a7); USED(a8); USED(a9); - r1 = runtime·syscall((void*)fn, nargs, &a1, &err); - r2 = 0; + c.fn = (void*)fn; + c.n = nargs; + c.args = &a1; + runtime·cgocall(runtime·asmstdcall, &c); + err = c.err; + r1 = c.r1; + r2 = c.r2; } func Syscall12(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr, a10 uintptr, a11 uintptr, a12 uintptr) (r1 uintptr, r2 uintptr, err uintptr) { + WinCall c; + USED(a2); USED(a3); USED(a4); @@ -66,6 +102,11 @@ func Syscall12(fn uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 USED(a10); USED(a11); USED(a12); - r1 = runtime·syscall((void*)fn, nargs, &a1, &err); - r2 = 0; + c.fn = (void*)fn; + c.n = nargs; + c.args = &a1; + runtime·cgocall(runtime·asmstdcall, &c); + err = c.err; + r1 = c.r1; + r2 = c.r2; } diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index b76eaac59..97a42d73a 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -6,49 +6,67 @@ #include "type.h" #include "defs.h" #include "os.h" -#include "cgocall.h" #pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll" #pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll" #pragma dynimport runtime·CreateThread CreateThread "kernel32.dll" +#pragma dynimport runtime·CreateWaitableTimer CreateWaitableTimerA "kernel32.dll" +#pragma dynimport runtime·DuplicateHandle DuplicateHandle "kernel32.dll" #pragma dynimport runtime·ExitProcess ExitProcess "kernel32.dll" #pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll" #pragma dynimport runtime·GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll" #pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll" #pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll" +#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll" #pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll" #pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll" #pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll" +#pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll" #pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll" #pragma dynimport runtime·SetEvent SetEvent "kernel32.dll" +#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll" +#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll" +#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll" +#pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll" #pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll" #pragma dynimport runtime·WriteFile WriteFile "kernel32.dll" extern void *runtime·CloseHandle; extern void *runtime·CreateEvent; extern void *runtime·CreateThread; +extern void *runtime·CreateWaitableTimer; +extern void *runtime·DuplicateHandle; extern void *runtime·ExitProcess; extern void *runtime·FreeEnvironmentStringsW; extern void *runtime·GetEnvironmentStringsW; extern void *runtime·GetProcAddress; extern void *runtime·GetStdHandle; +extern void *runtime·GetThreadContext; extern void *runtime·LoadLibraryEx; extern void *runtime·QueryPerformanceCounter; extern void *runtime·QueryPerformanceFrequency; +extern void *runtime·ResumeThread; extern void *runtime·SetConsoleCtrlHandler; extern void *runtime·SetEvent; +extern void *runtime·SetThreadPriority; +extern void *runtime·SetWaitableTimer; +extern void *runtime·SuspendThread; +extern void *runtime·timeBeginPeriod; extern void *runtime·WaitForSingleObject; extern void *runtime·WriteFile; static int64 timerfreq; -static void destroylock(Lock *l); void runtime·osinit(void) { + // -1 = current process, -2 = current thread + runtime·stdcall(runtime·DuplicateHandle, 7, + (uintptr)-1, (uintptr)-2, (uintptr)-1, &m->thread, + (uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS); runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq); runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1); - runtime·destroylock = destroylock; + runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1); } void @@ -121,22 +139,50 @@ initevent(void **pevent) } } +#define LOCK_HELD ((M*)-1) + static void eventlock(Lock *l) { // Allocate event if needed. - if(l->event == 0) - initevent(&l->event); + if(m->event == nil) + initevent(&m->event); + + for(;;) { + m->nextwaitm = runtime·atomicloadp(&l->waitm); + if(m->nextwaitm == nil) { + if(runtime·casp(&l->waitm, nil, LOCK_HELD)) + return; + // Someone else has it. + // l->waitm points to a linked list of M's waiting + // for this lock, chained through m->nextwaitm. + // Queue this M. + } else if(runtime·casp(&l->waitm, m->nextwaitm, m)) + break; + } - if(runtime·xadd(&l->key, 1) > 1) // someone else has it; wait - runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, (uintptr)-1); + // Wait. + runtime·stdcall(runtime·WaitForSingleObject, 2, m->event, (uintptr)-1); } static void eventunlock(Lock *l) { - if(runtime·xadd(&l->key, -1) > 0) // someone else is waiting - runtime·stdcall(runtime·SetEvent, 1, l->event); + M *mp; + + for(;;) { + mp = runtime·atomicloadp(&l->waitm); + if(mp == LOCK_HELD) { + if(runtime·casp(&l->waitm, LOCK_HELD, nil)) + return; + // Other M's are waiting for the lock. + // Dequeue a M. + } else if(runtime·casp(&l->waitm, mp, mp->nextwaitm)) + break; + } + + // Wake that M. + runtime·stdcall(runtime·SetEvent, 1, mp->event); } void @@ -157,17 +203,10 @@ runtime·unlock(Lock *l) eventunlock(l); } -static void -destroylock(Lock *l) -{ - if(l->event != 0) - runtime·stdcall(runtime·CloseHandle, 1, l->event); -} - void runtime·noteclear(Note *n) { - n->lock.key = 0; // memset(n, 0, sizeof *n) + n->lock.waitm = nil; eventlock(&n->lock); } @@ -193,11 +232,13 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) USED(g); // assuming g = m->g0 USED(fn); // assuming fn = mstart - thandle = runtime·stdcall(runtime·CreateThread, 6, (uintptr)0, (uintptr)0, runtime·tstart_stdcall, m, (uintptr)0, (uintptr)0); - if(thandle == 0) { + thandle = runtime·stdcall(runtime·CreateThread, 6, + nil, nil, runtime·tstart_stdcall, m, nil, nil); + if(thandle == nil) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); runtime·throw("runtime.newosproc"); } + runtime·atomicstorep(&m->thread, thandle); } // Called to initialize a new m (including the bootstrap m). @@ -228,21 +269,7 @@ runtime·stdcall(void *fn, int32 count, ...) c.n = count; c.args = (uintptr*)&count + 1; runtime·asmcgocall(runtime·asmstdcall, &c); - return (void*)c.r; -} - -uintptr -runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err) -{ - WinCall c; - - c.fn = fn; - c.n = nargs; - c.args = args; - runtime·cgocall(runtime·asmstdcall, &c); - if(err) - *err = c.err; - return c.r; + return (void*)c.r1; } uint32 @@ -320,6 +347,89 @@ runtime·ctrlhandler1(uint32 type) return 0; } +extern void runtime·dosigprof(Context *r, G *gp); +extern void runtime·profileloop(void); +static void *profiletimer; + +static void +profilem(M *mp) +{ + extern M runtime·m0; + extern uint32 runtime·tls0[]; + byte rbuf[sizeof(Context)+15]; + Context *r; + void *tls; + G *gp; + + tls = mp->tls; + if(mp == &runtime·m0) + tls = runtime·tls0; + gp = *(G**)tls; + + if(gp != nil && gp != mp->g0 && gp->status != Gsyscall) { + // align Context to 16 bytes + r = (Context*)((uintptr)(&rbuf[15]) & ~15); + r->ContextFlags = CONTEXT_CONTROL; + runtime·stdcall(runtime·GetThreadContext, 2, mp->thread, r); + runtime·dosigprof(r, gp); + } +} + +void +runtime·profileloop1(void) +{ + M *mp, *allm; + void *thread; + + runtime·stdcall(runtime·SetThreadPriority, 2, + (uintptr)-2, (uintptr)THREAD_PRIORITY_HIGHEST); + + for(;;) { + runtime·stdcall(runtime·WaitForSingleObject, 2, profiletimer, (uintptr)-1); + allm = runtime·atomicloadp(&runtime·allm); + for(mp = allm; mp != nil; mp = mp->alllink) { + thread = runtime·atomicloadp(&mp->thread); + if(thread == nil) + continue; + runtime·stdcall(runtime·SuspendThread, 1, thread); + if(mp->profilehz != 0) + profilem(mp); + runtime·stdcall(runtime·ResumeThread, 1, thread); + } + } +} + +void +runtime·resetcpuprofiler(int32 hz) +{ + static Lock lock; + void *timer, *thread; + int32 ms; + int64 due; + + runtime·lock(&lock); + if(profiletimer == nil) { + timer = runtime·stdcall(runtime·CreateWaitableTimer, 3, nil, nil, nil); + runtime·atomicstorep(&profiletimer, timer); + thread = runtime·stdcall(runtime·CreateThread, 6, + nil, nil, runtime·profileloop, nil, nil, nil); + runtime·stdcall(runtime·CloseHandle, 1, thread); + } + runtime·unlock(&lock); + + ms = 0; + due = 1LL<<63; + if(hz > 0) { + ms = 1000 / hz; + if(ms == 0) + ms = 1; + due = ms * -10000; + } + runtime·stdcall(runtime·SetWaitableTimer, 6, + profiletimer, &due, (uintptr)ms, nil, nil, nil); + runtime·atomicstore((uint32*)&m->profilehz, hz); +} + void os·sigpipe(void) { diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s index 4cab42654..d149eb66a 100644 --- a/src/pkg/sync/atomic/asm_386.s +++ b/src/pkg/sync/atomic/asm_386.s @@ -98,6 +98,20 @@ TEXT ·LoadUint32(SB),7,$0 MOVL AX, ret+4(FP) RET +TEXT ·LoadInt64(SB),7,$0 + JMP ·LoadUint64(SB) + +TEXT ·LoadUint64(SB),7,$0 + MOVL addrptr+0(FP), AX + // MOVQ and EMMS were introduced on the Pentium MMX. + // MOVQ (%EAX), %MM0 + BYTE $0x0f; BYTE $0x6f; BYTE $0x00 + // MOVQ %MM0, 0x8(%ESP) + BYTE $0x0f; BYTE $0x7f; BYTE $0x44; BYTE $0x24; BYTE $0x08 + // EMMS + BYTE $0x0F; BYTE $0x77 + RET + TEXT ·LoadUintptr(SB),7,$0 JMP ·LoadUint32(SB) @@ -113,6 +127,25 @@ TEXT ·StoreUint32(SB),7,$0 XCHGL AX, 0(BP) RET +TEXT ·StoreInt64(SB),7,$0 + JMP ·StoreUint64(SB) + +TEXT ·StoreUint64(SB),7,$0 + MOVL addrptr+0(FP), AX + // MOVQ and EMMS were introduced on the Pentium MMX. + // MOVQ 0x8(%ESP), %MM0 + BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08 + // MOVQ %MM0, (%EAX) + BYTE $0x0f; BYTE $0x7f; BYTE $0x00 + // EMMS + BYTE $0x0F; BYTE $0x77 + // This is essentially a no-op, but it provides required memory fencing. + // It can be replaced with MFENCE, but MFENCE was introduced only on the Pentium4 (SSE2). + XORL AX, AX + LOCK + XADDL AX, (SP) + RET + TEXT ·StoreUintptr(SB),7,$0 JMP ·StoreUint32(SB) diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s index d903f365a..6f8bde068 100644 --- a/src/pkg/sync/atomic/asm_amd64.s +++ b/src/pkg/sync/atomic/asm_amd64.s @@ -70,6 +70,15 @@ TEXT ·LoadUint32(SB),7,$0 MOVL AX, ret+8(FP) RET +TEXT ·LoadInt64(SB),7,$0 + JMP ·LoadUint64(SB) + +TEXT ·LoadUint64(SB),7,$0 + MOVQ addrptr+0(FP), AX + MOVQ 0(AX), AX + MOVQ AX, ret+8(FP) + RET + TEXT ·LoadUintptr(SB),7,$0 JMP ·LoadPointer(SB) @@ -88,6 +97,15 @@ TEXT ·StoreUint32(SB),7,$0 XCHGL AX, 0(BP) RET +TEXT ·StoreInt64(SB),7,$0 + JMP ·StoreUint64(SB) + +TEXT ·StoreUint64(SB),7,$0 + MOVQ addrptr+0(FP), BP + MOVQ val+8(FP), AX + XCHGQ AX, 0(BP) + RET + TEXT ·StoreUintptr(SB),7,$0 JMP ·StorePointer(SB) diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s index 95e2f5be4..2d10a922b 100644 --- a/src/pkg/sync/atomic/asm_arm.s +++ b/src/pkg/sync/atomic/asm_arm.s @@ -79,6 +79,30 @@ add64loop: MOVW R5, rethi+16(FP) RET +TEXT ·armLoadUint64(SB),7,$0 + BL fastCheck64<>(SB) + MOVW addrptr+0(FP), R1 +load64loop: + LDREXD (R1), R2 // loads R2 and R3 + STREXD R2, (R1), R0 // stores R2 and R3 + CMP $0, R0 + BNE load64loop + MOVW R2, vallo+4(FP) + MOVW R3, valhi+8(FP) + RET + +TEXT ·armStoreUint64(SB),7,$0 + BL fastCheck64<>(SB) + MOVW addrptr+0(FP), R1 + MOVW vallo+4(FP), R2 + MOVW valhi+8(FP), R3 +store64loop: + LDREXD (R1), R4 // loads R4 and R5 + STREXD R2, (R1), R0 // stores R2 and R3 + CMP $0, R0 + BNE store64loop + RET + // Check for broken 64-bit LDREXD as found in QEMU. // LDREXD followed by immediate STREXD should succeed. // If it fails, try a few times just to be sure (maybe our thread got diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s index ff44191c7..25dc85804 100644 --- a/src/pkg/sync/atomic/asm_linux_arm.s +++ b/src/pkg/sync/atomic/asm_linux_arm.s @@ -100,6 +100,12 @@ loadloop1: MOVW R1, val+4(FP) RET +TEXT ·LoadInt64(SB),7,$0 + B ·armLoadUint64(SB) + +TEXT ·LoadUint64(SB),7,$0 + B ·armLoadUint64(SB) + TEXT ·LoadUintptr(SB),7,$0 B ·LoadUint32(SB) @@ -118,6 +124,12 @@ storeloop1: BCC storeloop1 RET +TEXT ·StoreInt64(SB),7,$0 + B ·armStoreUint64(SB) + +TEXT ·StoreUint64(SB),7,$0 + B ·armStoreUint64(SB) + TEXT ·StoreUintptr(SB),7,$0 B ·StoreUint32(SB) diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go index d3fc1387c..02ee24b35 100644 --- a/src/pkg/sync/atomic/atomic_test.go +++ b/src/pkg/sync/atomic/atomic_test.go @@ -379,6 +379,54 @@ func TestLoadUint32(t *testing.T) { } } +func TestLoadInt64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + var x struct { + before int64 + i int64 + after int64 + } + x.before = magic64 + x.after = magic64 + for delta := int64(1); delta+delta > delta; delta += delta { + k := LoadInt64(&x.i) + if k != x.i { + t.Fatalf("delta=%d i=%d k=%d", delta, x.i, k) + } + x.i += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestLoadUint64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + var x struct { + before uint64 + i uint64 + after uint64 + } + x.before = magic64 + x.after = magic64 + for delta := uint64(1); delta+delta > delta; delta += delta { + k := LoadUint64(&x.i) + if k != x.i { + t.Fatalf("delta=%d i=%d k=%d", delta, x.i, k) + } + x.i += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + func TestLoadUintptr(t *testing.T) { var x struct { before uintptr @@ -465,6 +513,56 @@ func TestStoreUint32(t *testing.T) { } } +func TestStoreInt64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + var x struct { + before int64 + i int64 + after int64 + } + x.before = magic64 + x.after = magic64 + v := int64(0) + for delta := int64(1); delta+delta > delta; delta += delta { + StoreInt64(&x.i, v) + if x.i != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v) + } + v += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestStoreUint64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + var x struct { + before uint64 + i uint64 + after uint64 + } + x.before = magic64 + x.after = magic64 + v := uint64(0) + for delta := uint64(1); delta+delta > delta; delta += delta { + StoreUint64(&x.i, v) + if x.i != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v) + } + v += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + func TestStoreUintptr(t *testing.T) { var x struct { before uintptr @@ -777,7 +875,7 @@ func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { - t.Fatalf("LoadInt32: %#x != %#x", vlo, vhi) + t.Fatalf("Int32: %#x != %#x", vlo, vhi) } new := v + 1 + 1<<16 if vlo == 1e4 { @@ -792,7 +890,7 @@ func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { - t.Fatalf("LoadUint32: %#x != %#x", vlo, vhi) + t.Fatalf("Uint32: %#x != %#x", vlo, vhi) } new := v + 1 + 1<<16 if vlo == 1e4 { @@ -801,6 +899,30 @@ func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) { StoreUint32(val, new) } +func hammerStoreLoadInt64(t *testing.T, valp unsafe.Pointer) { + val := (*int64)(valp) + v := LoadInt64(val) + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Int64: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<32 + StoreInt64(val, new) +} + +func hammerStoreLoadUint64(t *testing.T, valp unsafe.Pointer) { + val := (*uint64)(valp) + v := LoadUint64(val) + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Uint64: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<32 + StoreUint64(val, new) +} + func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) { val := (*uintptr)(valp) var test64 uint64 = 1 << 50 @@ -811,7 +933,7 @@ func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { - t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi) + t.Fatalf("Uintptr: %#x != %#x", vlo, vhi) } new = v + 1 + 1<<16 if vlo == 1e4 { @@ -821,7 +943,7 @@ func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 32) - 1) vhi := v >> 32 if vlo != vhi { - t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi) + t.Fatalf("Uintptr: %#x != %#x", vlo, vhi) } inc := uint64(1 + 1<<32) new = v + uintptr(inc) @@ -839,7 +961,7 @@ func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { - t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi) + t.Fatalf("Pointer: %#x != %#x", vlo, vhi) } new = v + 1 + 1<<16 if vlo == 1e4 { @@ -849,7 +971,7 @@ func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) { vlo := v & ((1 << 32) - 1) vhi := v >> 32 if vlo != vhi { - t.Fatalf("LoadUintptr: %#x != %#x", vlo, vhi) + t.Fatalf("Pointer: %#x != %#x", vlo, vhi) } inc := uint64(1 + 1<<32) new = v + uintptr(inc) @@ -858,8 +980,12 @@ func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) { } func TestHammerStoreLoad(t *testing.T) { - tests := [...]func(*testing.T, unsafe.Pointer){hammerStoreLoadInt32, hammerStoreLoadUint32, - hammerStoreLoadUintptr, hammerStoreLoadPointer} + var tests []func(*testing.T, unsafe.Pointer) + tests = append(tests, hammerStoreLoadInt32, hammerStoreLoadUint32, + hammerStoreLoadUintptr, hammerStoreLoadPointer) + if test64err == nil { + tests = append(tests, hammerStoreLoadInt64, hammerStoreLoadUint64) + } n := int(1e6) if testing.Short() { n = int(1e4) @@ -883,11 +1009,11 @@ func TestHammerStoreLoad(t *testing.T) { } } -func TestStoreLoadSeqCst(t *testing.T) { +func TestStoreLoadSeqCst32(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) - N := int32(1e6) + N := int32(1e3) if testing.Short() { - N = int32(1e5) + N = int32(1e2) } c := make(chan bool, 2) X := [2]int32{} @@ -898,13 +1024,13 @@ func TestStoreLoadSeqCst(t *testing.T) { for i := int32(1); i < N; i++ { StoreInt32(&X[me], i) my := LoadInt32(&X[he]) - ack[me][i%3] = my - for w := 1; ack[he][i%3] == -1; w++ { + StoreInt32(&ack[me][i%3], my) + for w := 1; LoadInt32(&ack[he][i%3]) == -1; w++ { if w%1000 == 0 { runtime.Gosched() } } - his := ack[he][i%3] + his := LoadInt32(&ack[he][i%3]) if (my != i && my != i-1) || (his != i && his != i-1) { t.Fatalf("invalid values: %d/%d (%d)", my, his, i) } @@ -919,3 +1045,132 @@ func TestStoreLoadSeqCst(t *testing.T) { <-c <-c } + +func TestStoreLoadSeqCst64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + N := int64(1e3) + if testing.Short() { + N = int64(1e2) + } + c := make(chan bool, 2) + X := [2]int64{} + ack := [2][3]int64{{-1, -1, -1}, {-1, -1, -1}} + for p := 0; p < 2; p++ { + go func(me int) { + he := 1 - me + for i := int64(1); i < N; i++ { + StoreInt64(&X[me], i) + my := LoadInt64(&X[he]) + StoreInt64(&ack[me][i%3], my) + for w := 1; LoadInt64(&ack[he][i%3]) == -1; w++ { + if w%1000 == 0 { + runtime.Gosched() + } + } + his := LoadInt64(&ack[he][i%3]) + if (my != i && my != i-1) || (his != i && his != i-1) { + t.Fatalf("invalid values: %d/%d (%d)", my, his, i) + } + if my != i && his != i { + t.Fatalf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i) + } + ack[me][(i-1)%3] = -1 + } + c <- true + }(p) + } + <-c + <-c +} + +func TestStoreLoadRelAcq32(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + N := int32(1e3) + if testing.Short() { + N = int32(1e2) + } + c := make(chan bool, 2) + type Data struct { + signal int32 + pad1 [128]int8 + data1 int32 + pad2 [128]int8 + data2 float32 + } + var X Data + for p := int32(0); p < 2; p++ { + go func(p int32) { + for i := int32(1); i < N; i++ { + if (i+p)%2 == 0 { + X.data1 = i + X.data2 = float32(i) + StoreInt32(&X.signal, i) + } else { + for w := 1; LoadInt32(&X.signal) != i; w++ { + if w%1000 == 0 { + runtime.Gosched() + } + } + d1 := X.data1 + d2 := X.data2 + if d1 != i || d2 != float32(i) { + t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i) + } + } + } + c <- true + }(p) + } + <-c + <-c +} + +func TestStoreLoadRelAcq64(t *testing.T) { + if test64err != nil { + t.Logf("Skipping 64-bit tests: %v", test64err) + return + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + N := int64(1e3) + if testing.Short() { + N = int64(1e2) + } + c := make(chan bool, 2) + type Data struct { + signal int64 + pad1 [128]int8 + data1 int64 + pad2 [128]int8 + data2 float64 + } + var X Data + for p := int64(0); p < 2; p++ { + go func(p int64) { + for i := int64(1); i < N; i++ { + if (i+p)%2 == 0 { + X.data1 = i + X.data2 = float64(i) + StoreInt64(&X.signal, i) + } else { + for w := 1; LoadInt64(&X.signal) != i; w++ { + if w%1000 == 0 { + runtime.Gosched() + } + } + d1 := X.data1 + d2 := X.data2 + if d1 != i || d2 != float64(i) { + t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i) + } + } + } + c <- true + }(p) + } + <-c + <-c +} diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go index 987f8c93d..ecb4808ce 100644 --- a/src/pkg/sync/atomic/doc.go +++ b/src/pkg/sync/atomic/doc.go @@ -28,7 +28,7 @@ import ( // BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11. // -// On x86-32, the 64-bit functions use instructions unavailable before the Pentium. +// On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. // CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value. func CompareAndSwapInt32(val *int32, old, new int32) (swapped bool) @@ -66,9 +66,15 @@ func AddUintptr(val *uintptr, delta uintptr) (new uintptr) // LoadInt32 atomically loads *addr. func LoadInt32(addr *int32) (val int32) +// LoadInt64 atomically loads *addr. +func LoadInt64(addr *int64) (val int64) + // LoadUint32 atomically loads *addr. func LoadUint32(addr *uint32) (val uint32) +// LoadUint64 atomically loads *addr. +func LoadUint64(addr *uint64) (val uint64) + // LoadUintptr atomically loads *addr. func LoadUintptr(addr *uintptr) (val uintptr) @@ -78,9 +84,15 @@ func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) // StoreInt32 atomically stores val into *addr. func StoreInt32(addr *int32, val int32) +// StoreInt64 atomically stores val into *addr. +func StoreInt64(addr *int64, val int64) + // StoreUint32 atomically stores val into *addr. func StoreUint32(addr *uint32, val uint32) +// StoreUint64 atomically stores val into *addr. +func StoreUint64(addr *uint64, val uint64) + // StoreUintptr atomically stores val into *addr. func StoreUintptr(addr *uintptr, val uintptr) diff --git a/src/pkg/syscall/bpf_bsd.go b/src/pkg/syscall/bpf_bsd.go index 1eac9a3d8..06a2953e7 100644 --- a/src/pkg/syscall/bpf_bsd.go +++ b/src/pkg/syscall/bpf_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + // Berkeley packet filter for BSD variants package syscall diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go index 94f075622..2399c89cc 100644 --- a/src/pkg/syscall/exec_unix.go +++ b/src/pkg/syscall/exec_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Fork, exec, wait, etc. package syscall diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh index 9384e9bec..9936b11ac 100755 --- a/src/pkg/syscall/mkerrors.sh +++ b/src/pkg/syscall/mkerrors.sh @@ -40,6 +40,7 @@ includes_Linux=' #include <linux/wait.h> #include <net/if.h> #include <net/if_arp.h> +#include <net/route.h> #include <netpacket/packet.h> ' @@ -168,7 +169,7 @@ done $2 ~ /^LINUX_REBOOT_CMD_/ || $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || $2 !~ "NLA_TYPE_MASK" && - $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RTM|RTN|RTPROT|RTA|RTAX|RTNH|ARPHRD|ETH_P)_/ || + $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ || $2 ~ /^SIOC/ || $2 ~ /^TIOC/ || $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || diff --git a/src/pkg/syscall/mkerrors_windows.sh b/src/pkg/syscall/mkerrors_windows.sh index af95edd00..a76f250fd 100755 --- a/src/pkg/syscall/mkerrors_windows.sh +++ b/src/pkg/syscall/mkerrors_windows.sh @@ -76,7 +76,7 @@ done # These are go errors that will be mapped directly to windows errors goerrors=' ENOENT:ERROR_FILE_NOT_FOUND -ENOTDIR:ERROR_DIRECTORY +ENOTDIR:ERROR_PATH_NOT_FOUND ' # Pull out just the error names for later. diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go index 22a0a4f80..f6b124b64 100644 --- a/src/pkg/syscall/route_bsd.go +++ b/src/pkg/syscall/route_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + // Routing sockets and messages package syscall diff --git a/src/pkg/syscall/sockcmsg_unix.go b/src/pkg/syscall/sockcmsg_unix.go index b437560e7..c9872aeba 100644 --- a/src/pkg/syscall/sockcmsg_unix.go +++ b/src/pkg/syscall/sockcmsg_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Socket control messages package syscall diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go index f59e8b109..59c6f4560 100644 --- a/src/pkg/syscall/syscall_bsd.go +++ b/src/pkg/syscall/syscall_bsd.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd openbsd + // BSD system call wrappers shared by *BSD based systems // including OS X (Darwin) and FreeBSD. Like the other // syscall_*.go files it is compiled as Go code but also diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go index c298b91b4..1590b6d4f 100644 --- a/src/pkg/syscall/syscall_unix.go +++ b/src/pkg/syscall/syscall_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package syscall import ( diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 7bc26d3cf..26939cc8d 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -220,7 +220,7 @@ func NewCallback(fn interface{}) uintptr //sys FlushViewOfFile(addr uintptr, length uintptr) (errno int) //sys VirtualLock(addr uintptr, length uintptr) (errno int) //sys VirtualUnlock(addr uintptr, length uintptr) (errno int) -//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = wsock32.TransmitFile +//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = mswsock.TransmitFile // syscall interface implementation for other packages @@ -480,20 +480,20 @@ func Chmod(path string, mode uint32) (errno int) { // net api calls -//sys WSAStartup(verreq uint32, data *WSAData) (sockerrno int) = wsock32.WSAStartup -//sys WSACleanup() (errno int) [failretval==-1] = wsock32.WSACleanup +//sys WSAStartup(verreq uint32, data *WSAData) (sockerrno int) = ws2_32.WSAStartup +//sys WSACleanup() (errno int) [failretval==-1] = ws2_32.WSACleanup //sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) [failretval==-1] = ws2_32.WSAIoctl -//sys socket(af int32, typ int32, protocol int32) (handle Handle, errno int) [failretval==InvalidHandle] = wsock32.socket -//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = wsock32.setsockopt -//sys bind(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.bind -//sys connect(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.connect -//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getsockname -//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getpeername -//sys listen(s Handle, backlog int32) (errno int) [failretval==-1] = wsock32.listen -//sys shutdown(s Handle, how int32) (errno int) [failretval==-1] = wsock32.shutdown -//sys Closesocket(s Handle) (errno int) [failretval==-1] = wsock32.closesocket -//sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx -//sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = wsock32.GetAcceptExSockaddrs +//sys socket(af int32, typ int32, protocol int32) (handle Handle, errno int) [failretval==InvalidHandle] = ws2_32.socket +//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = ws2_32.setsockopt +//sys bind(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = ws2_32.bind +//sys connect(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = ws2_32.connect +//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = ws2_32.getsockname +//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = ws2_32.getpeername +//sys listen(s Handle, backlog int32) (errno int) [failretval==-1] = ws2_32.listen +//sys shutdown(s Handle, how int32) (errno int) [failretval==-1] = ws2_32.shutdown +//sys Closesocket(s Handle) (errno int) [failretval==-1] = ws2_32.closesocket +//sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = mswsock.AcceptEx +//sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = mswsock.GetAcceptExSockaddrs //sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv //sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend //sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecvFrom @@ -697,8 +697,8 @@ type Linger struct { } const ( - IP_ADD_MEMBERSHIP = iota - IP_DROP_MEMBERSHIP + IP_ADD_MEMBERSHIP = 0xc + IP_DROP_MEMBERSHIP = 0xd ) type IPMreq struct { @@ -711,8 +711,10 @@ type IPv6Mreq struct { Interface uint32 } -func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (errno int) { return EWINDOWS } -func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (errno int) { return EWINDOWS } +func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (errno int) { return EWINDOWS } +func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (errno int) { + return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq))) +} func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (errno int) { return EWINDOWS } func BindToDevice(fd Handle, device string) (errno int) { return EWINDOWS } diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go index f482096e0..dba66e661 100644 --- a/src/pkg/syscall/zerrors_linux_386.go +++ b/src/pkg/syscall/zerrors_linux_386.go @@ -828,6 +828,42 @@ const ( RTAX_WINDOW = 0x3 RTA_ALIGNTO = 0x4 RTA_MAX = 0xf + RTCF_DIRECTSRC = 0x4000000 + RTCF_DOREDIRECT = 0x1000000 + RTCF_LOG = 0x2000000 + RTCF_MASQ = 0x400000 + RTCF_NAT = 0x800000 + RTCF_VALVE = 0x200000 + RTF_ADDRCLASSMASK = 0xf8000000 + RTF_ADDRCONF = 0x40000 + RTF_ALLONLINK = 0x20000 + RTF_BROADCAST = 0x10000000 + RTF_CACHE = 0x1000000 + RTF_DEFAULT = 0x10000 + RTF_DYNAMIC = 0x10 + RTF_FLOW = 0x2000000 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_INTERFACE = 0x40000000 + RTF_IRTT = 0x100 + RTF_LINKRT = 0x100000 + RTF_LOCAL = 0x80000000 + RTF_MODIFIED = 0x20 + RTF_MSS = 0x40 + RTF_MTU = 0x40 + RTF_MULTICAST = 0x20000000 + RTF_NAT = 0x8000000 + RTF_NOFORWARD = 0x1000 + RTF_NONEXTHOP = 0x200000 + RTF_NOPMTUDISC = 0x4000 + RTF_POLICY = 0x4000000 + RTF_REINSTATE = 0x8 + RTF_REJECT = 0x200 + RTF_STATIC = 0x400 + RTF_THROW = 0x2000 + RTF_UP = 0x1 + RTF_WINDOW = 0x80 + RTF_XRESOLVE = 0x800 RTM_BASE = 0x10 RTM_DELACTION = 0x31 RTM_DELADDR = 0x15 @@ -895,6 +931,11 @@ const ( RTPROT_UNSPEC = 0 RTPROT_XORP = 0xe RTPROT_ZEBRA = 0xb + RT_CLASS_DEFAULT = 0xfd + RT_CLASS_LOCAL = 0xff + RT_CLASS_MAIN = 0xfe + RT_CLASS_MAX = 0xff + RT_CLASS_UNSPEC = 0 SCM_CREDENTIALS = 0x2 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x1d diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go index 5d1f31079..1f29287e0 100644 --- a/src/pkg/syscall/zerrors_linux_amd64.go +++ b/src/pkg/syscall/zerrors_linux_amd64.go @@ -829,6 +829,42 @@ const ( RTAX_WINDOW = 0x3 RTA_ALIGNTO = 0x4 RTA_MAX = 0xf + RTCF_DIRECTSRC = 0x4000000 + RTCF_DOREDIRECT = 0x1000000 + RTCF_LOG = 0x2000000 + RTCF_MASQ = 0x400000 + RTCF_NAT = 0x800000 + RTCF_VALVE = 0x200000 + RTF_ADDRCLASSMASK = 0xf8000000 + RTF_ADDRCONF = 0x40000 + RTF_ALLONLINK = 0x20000 + RTF_BROADCAST = 0x10000000 + RTF_CACHE = 0x1000000 + RTF_DEFAULT = 0x10000 + RTF_DYNAMIC = 0x10 + RTF_FLOW = 0x2000000 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_INTERFACE = 0x40000000 + RTF_IRTT = 0x100 + RTF_LINKRT = 0x100000 + RTF_LOCAL = 0x80000000 + RTF_MODIFIED = 0x20 + RTF_MSS = 0x40 + RTF_MTU = 0x40 + RTF_MULTICAST = 0x20000000 + RTF_NAT = 0x8000000 + RTF_NOFORWARD = 0x1000 + RTF_NONEXTHOP = 0x200000 + RTF_NOPMTUDISC = 0x4000 + RTF_POLICY = 0x4000000 + RTF_REINSTATE = 0x8 + RTF_REJECT = 0x200 + RTF_STATIC = 0x400 + RTF_THROW = 0x2000 + RTF_UP = 0x1 + RTF_WINDOW = 0x80 + RTF_XRESOLVE = 0x800 RTM_BASE = 0x10 RTM_DELACTION = 0x31 RTM_DELADDR = 0x15 @@ -896,6 +932,11 @@ const ( RTPROT_UNSPEC = 0 RTPROT_XORP = 0xe RTPROT_ZEBRA = 0xb + RT_CLASS_DEFAULT = 0xfd + RT_CLASS_LOCAL = 0xff + RT_CLASS_MAIN = 0xfe + RT_CLASS_MAX = 0xff + RT_CLASS_UNSPEC = 0 SCM_CREDENTIALS = 0x2 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x1d diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go index bebc00a4c..da91b2aad 100644 --- a/src/pkg/syscall/zerrors_linux_arm.go +++ b/src/pkg/syscall/zerrors_linux_arm.go @@ -822,6 +822,42 @@ const ( RTAX_WINDOW = 0x3 RTA_ALIGNTO = 0x4 RTA_MAX = 0xf + RTCF_DIRECTSRC = 0x4000000 + RTCF_DOREDIRECT = 0x1000000 + RTCF_LOG = 0x2000000 + RTCF_MASQ = 0x400000 + RTCF_NAT = 0x800000 + RTCF_VALVE = 0x200000 + RTF_ADDRCLASSMASK = 0xf8000000 + RTF_ADDRCONF = 0x40000 + RTF_ALLONLINK = 0x20000 + RTF_BROADCAST = 0x10000000 + RTF_CACHE = 0x1000000 + RTF_DEFAULT = 0x10000 + RTF_DYNAMIC = 0x10 + RTF_FLOW = 0x2000000 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_INTERFACE = 0x40000000 + RTF_IRTT = 0x100 + RTF_LINKRT = 0x100000 + RTF_LOCAL = 0x80000000 + RTF_MODIFIED = 0x20 + RTF_MSS = 0x40 + RTF_MTU = 0x40 + RTF_MULTICAST = 0x20000000 + RTF_NAT = 0x8000000 + RTF_NOFORWARD = 0x1000 + RTF_NONEXTHOP = 0x200000 + RTF_NOPMTUDISC = 0x4000 + RTF_POLICY = 0x4000000 + RTF_REINSTATE = 0x8 + RTF_REJECT = 0x200 + RTF_STATIC = 0x400 + RTF_THROW = 0x2000 + RTF_UP = 0x1 + RTF_WINDOW = 0x80 + RTF_XRESOLVE = 0x800 RTM_BASE = 0x10 RTM_DELACTION = 0x31 RTM_DELADDR = 0x15 @@ -889,6 +925,11 @@ const ( RTPROT_UNSPEC = 0 RTPROT_XORP = 0xe RTPROT_ZEBRA = 0xb + RT_CLASS_DEFAULT = 0xfd + RT_CLASS_LOCAL = 0xff + RT_CLASS_MAIN = 0xfe + RT_CLASS_MAX = 0xff + RT_CLASS_UNSPEC = 0 SCM_CREDENTIALS = 0x2 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x1d diff --git a/src/pkg/syscall/zerrors_windows.go b/src/pkg/syscall/zerrors_windows.go index ae4506fac..799ed490a 100644 --- a/src/pkg/syscall/zerrors_windows.go +++ b/src/pkg/syscall/zerrors_windows.go @@ -6,7 +6,7 @@ package syscall // Go names for Windows errors. const ( ENOENT = ERROR_FILE_NOT_FOUND - ENOTDIR = ERROR_DIRECTORY + ENOTDIR = ERROR_PATH_NOT_FOUND ) // Windows reserves errors >= 1<<29 for application use. diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index 24c82a479..845004aa3 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -9,7 +9,7 @@ var ( modkernel32 = NewLazyDLL("kernel32.dll") modadvapi32 = NewLazyDLL("advapi32.dll") modshell32 = NewLazyDLL("shell32.dll") - modwsock32 = NewLazyDLL("wsock32.dll") + modmswsock = NewLazyDLL("mswsock.dll") modws2_32 = NewLazyDLL("ws2_32.dll") moddnsapi = NewLazyDLL("dnsapi.dll") modiphlpapi = NewLazyDLL("iphlpapi.dll") @@ -79,21 +79,21 @@ var ( procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") procVirtualLock = modkernel32.NewProc("VirtualLock") procVirtualUnlock = modkernel32.NewProc("VirtualUnlock") - procTransmitFile = modwsock32.NewProc("TransmitFile") - procWSAStartup = modwsock32.NewProc("WSAStartup") - procWSACleanup = modwsock32.NewProc("WSACleanup") + procTransmitFile = modmswsock.NewProc("TransmitFile") + procWSAStartup = modws2_32.NewProc("WSAStartup") + procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") - procsocket = modwsock32.NewProc("socket") - procsetsockopt = modwsock32.NewProc("setsockopt") - procbind = modwsock32.NewProc("bind") - procconnect = modwsock32.NewProc("connect") - procgetsockname = modwsock32.NewProc("getsockname") - procgetpeername = modwsock32.NewProc("getpeername") - proclisten = modwsock32.NewProc("listen") - procshutdown = modwsock32.NewProc("shutdown") - procclosesocket = modwsock32.NewProc("closesocket") - procAcceptEx = modwsock32.NewProc("AcceptEx") - procGetAcceptExSockaddrs = modwsock32.NewProc("GetAcceptExSockaddrs") + procsocket = modws2_32.NewProc("socket") + procsetsockopt = modws2_32.NewProc("setsockopt") + procbind = modws2_32.NewProc("bind") + procconnect = modws2_32.NewProc("connect") + procgetsockname = modws2_32.NewProc("getsockname") + procgetpeername = modws2_32.NewProc("getpeername") + proclisten = modws2_32.NewProc("listen") + procshutdown = modws2_32.NewProc("shutdown") + procclosesocket = modws2_32.NewProc("closesocket") + procAcceptEx = modmswsock.NewProc("AcceptEx") + procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs") procWSARecv = modws2_32.NewProc("WSARecv") procWSASend = modws2_32.NewProc("WSASend") procWSARecvFrom = modws2_32.NewProc("WSARecvFrom") diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go index 06bb114ba..0904085b9 100644 --- a/src/pkg/syscall/zsyscall_windows_amd64.go +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -9,7 +9,7 @@ var ( modkernel32 = NewLazyDLL("kernel32.dll") modadvapi32 = NewLazyDLL("advapi32.dll") modshell32 = NewLazyDLL("shell32.dll") - modwsock32 = NewLazyDLL("wsock32.dll") + modmswsock = NewLazyDLL("mswsock.dll") modws2_32 = NewLazyDLL("ws2_32.dll") moddnsapi = NewLazyDLL("dnsapi.dll") modiphlpapi = NewLazyDLL("iphlpapi.dll") @@ -79,21 +79,21 @@ var ( procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") procVirtualLock = modkernel32.NewProc("VirtualLock") procVirtualUnlock = modkernel32.NewProc("VirtualUnlock") - procTransmitFile = modwsock32.NewProc("TransmitFile") - procWSAStartup = modwsock32.NewProc("WSAStartup") - procWSACleanup = modwsock32.NewProc("WSACleanup") + procTransmitFile = modmswsock.NewProc("TransmitFile") + procWSAStartup = modws2_32.NewProc("WSAStartup") + procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") - procsocket = modwsock32.NewProc("socket") - procsetsockopt = modwsock32.NewProc("setsockopt") - procbind = modwsock32.NewProc("bind") - procconnect = modwsock32.NewProc("connect") - procgetsockname = modwsock32.NewProc("getsockname") - procgetpeername = modwsock32.NewProc("getpeername") - proclisten = modwsock32.NewProc("listen") - procshutdown = modwsock32.NewProc("shutdown") - procclosesocket = modwsock32.NewProc("closesocket") - procAcceptEx = modwsock32.NewProc("AcceptEx") - procGetAcceptExSockaddrs = modwsock32.NewProc("GetAcceptExSockaddrs") + procsocket = modws2_32.NewProc("socket") + procsetsockopt = modws2_32.NewProc("setsockopt") + procbind = modws2_32.NewProc("bind") + procconnect = modws2_32.NewProc("connect") + procgetsockname = modws2_32.NewProc("getsockname") + procgetpeername = modws2_32.NewProc("getpeername") + proclisten = modws2_32.NewProc("listen") + procshutdown = modws2_32.NewProc("shutdown") + procclosesocket = modws2_32.NewProc("closesocket") + procAcceptEx = modmswsock.NewProc("AcceptEx") + procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs") procWSARecv = modws2_32.NewProc("WSARecv") procWSASend = modws2_32.NewProc("WSASend") procWSARecvFrom = modws2_32.NewProc("WSARecvFrom") diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go index 5b8c33a21..a3ef1ba43 100644 --- a/src/pkg/syscall/ztypes_windows.go +++ b/src/pkg/syscall/ztypes_windows.go @@ -11,7 +11,6 @@ const ( ERROR_MOD_NOT_FOUND = 126 ERROR_PROC_NOT_FOUND = 127 ERROR_ENVVAR_NOT_FOUND = 203 - ERROR_DIRECTORY = 267 ERROR_OPERATION_ABORTED = 995 ERROR_IO_PENDING = 997 ) diff --git a/src/pkg/template/Makefile b/src/pkg/template/Makefile index 3ed3b0330..730b287af 100644 --- a/src/pkg/template/Makefile +++ b/src/pkg/template/Makefile @@ -6,6 +6,7 @@ include ../../Make.inc TARG=template GOFILES=\ + doc.go\ exec.go\ funcs.go\ helper.go\ diff --git a/src/pkg/template/helper.go b/src/pkg/template/helper.go index c9b099856..1dc90f7ff 100644 --- a/src/pkg/template/helper.go +++ b/src/pkg/template/helper.go @@ -210,7 +210,8 @@ func ParseTemplateFiles(filenames ...string) (*Set, os.Error) { } // ParseTemplateGlob creates a set by parsing the files matched -// by the pattern, each of which defines a single template. Each +// by the pattern, each of which defines a single template. The pattern +// is processed by filepath.Glob and must match at least one file. Each // template will be named the base name of its file. // Unlike with ParseGlob, each file should be a stand-alone template // definition suitable for Template.Parse (not Set.Parse); that is, the @@ -225,6 +226,9 @@ func ParseTemplateGlob(pattern string) (*Set, os.Error) { if err != nil { return nil, err } + if len(filenames) == 0 { + return nil, fmt.Errorf("pattern matches no files: %#q", pattern) + } for _, filename := range filenames { t, err := ParseFile(filename) if err != nil { diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index 5ddd54812..0701cc925 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -296,9 +296,9 @@ func (t *Time) Format(layout string) string { case stdZeroMonth: p = zeroPad(t.Month) case stdWeekDay: - p = shortDayNames[t.Weekday] + p = shortDayNames[t.Weekday()] case stdLongWeekDay: - p = longDayNames[t.Weekday] + p = longDayNames[t.Weekday()] case stdDay: p = strconv.Itoa(t.Day) case stdUnderDay: @@ -485,7 +485,8 @@ func skip(value, prefix string) (string, os.Error) { // (such as having the wrong day of the week), the returned value will also // be inconsistent. In any case, the elements of the returned time will be // sane: hours in 0..23, minutes in 0..59, day of month in 1..31, etc. -// Years must be in the range 0000..9999. +// Years must be in the range 0000..9999. The day of the week is checked +// for syntax but it is otherwise ignored. func Parse(alayout, avalue string) (*Time, os.Error) { var t Time rangeErrString := "" // set if a value is out of range @@ -538,9 +539,10 @@ func Parse(alayout, avalue string) (*Time, os.Error) { rangeErrString = "month" } case stdWeekDay: - t.Weekday, value, err = lookup(shortDayNames, value) + // Ignore weekday except for error checking. + _, value, err = lookup(shortDayNames, value) case stdLongWeekDay: - t.Weekday, value, err = lookup(longDayNames, value) + _, value, err = lookup(longDayNames, value) case stdDay, stdUnderDay, stdZeroDay: if std == stdUnderDay && len(value) > 0 && value[0] == ' ' { value = value[1:] diff --git a/src/pkg/time/sys_unix.go b/src/pkg/time/sys_unix.go index 0f9128e20..0119bdf7b 100644 --- a/src/pkg/time/sys_unix.go +++ b/src/pkg/time/sys_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package time import ( diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go index 0e05da484..859b31672 100644 --- a/src/pkg/time/time.go +++ b/src/pkg/time/time.go @@ -22,7 +22,6 @@ type Time struct { Month, Day int // Jan-2 is 1, 2 Hour, Minute, Second int // 15:04:05 is 15, 4, 5. Nanosecond int // Fractional second. - Weekday int // Sunday, Monday, ... ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700 Zone string // e.g., "MST" } @@ -63,12 +62,6 @@ func SecondsToUTC(sec int64) *Time { t.Minute = int((sec / 60) % 60) t.Second = int(sec % 60) - // Day 0 = January 1, 1970 was a Thursday - t.Weekday = int((day + Thursday) % 7) - if t.Weekday < 0 { - t.Weekday += 7 - } - // Change day from 0 = 1970 to 0 = 2001, // to make leap year calculations easier // (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.) @@ -228,3 +221,19 @@ func (t *Time) Seconds() int64 { func (t *Time) Nanoseconds() int64 { return t.Seconds()*1e9 + int64(t.Nanosecond) } + +// Weekday returns the time's day of the week. Sunday is day 0. +func (t *Time) Weekday() int { + sec := t.Seconds() + int64(t.ZoneOffset) + day := sec / secondsPerDay + sec -= day * secondsPerDay + if sec < 0 { + day-- + } + // Day 0 = January 1, 1970 was a Thursday + weekday := int((day + Thursday) % 7) + if weekday < 0 { + weekday += 7 + } + return weekday +} diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index 07d759833..fe0f3482a 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -30,31 +30,31 @@ type TimeTest struct { } var utctests = []TimeTest{ - {0, Time{1970, 1, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}}, - {1221681866, Time{2008, 9, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}}, - {-1221681866, Time{1931, 4, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}}, - {-11644473600, Time{1601, 1, 1, 0, 0, 0, 0, Monday, 0, "UTC"}}, - {599529660, Time{1988, 12, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}}, - {978220860, Time{2000, 12, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}}, - {1e18, Time{31688740476, 10, 23, 1, 46, 40, 0, Friday, 0, "UTC"}}, - {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, 0, Tuesday, 0, "UTC"}}, - {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, 0, Sunday, 0, "UTC"}}, - {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, 0, Sunday, 0, "UTC"}}, + {0, Time{1970, 1, 1, 0, 0, 0, 0, 0, "UTC"}}, + {1221681866, Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}}, + {-1221681866, Time{1931, 4, 16, 3, 55, 34, 0, 0, "UTC"}}, + {-11644473600, Time{1601, 1, 1, 0, 0, 0, 0, 0, "UTC"}}, + {599529660, Time{1988, 12, 31, 0, 1, 0, 0, 0, "UTC"}}, + {978220860, Time{2000, 12, 31, 0, 1, 0, 0, 0, "UTC"}}, + {1e18, Time{31688740476, 10, 23, 1, 46, 40, 0, 0, "UTC"}}, + {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, 0, 0, "UTC"}}, + {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, 0, 0, "UTC"}}, + {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, 0, 0, "UTC"}}, } var nanoutctests = []TimeTest{ - {0, Time{1970, 1, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}}, - {1221681866, Time{2008, 9, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}}, + {0, Time{1970, 1, 1, 0, 0, 0, 1e8, 0, "UTC"}}, + {1221681866, Time{2008, 9, 17, 20, 4, 26, 2e8, 0, "UTC"}}, } var localtests = []TimeTest{ - {0, Time{1969, 12, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}}, - {1221681866, Time{2008, 9, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}}, + {0, Time{1969, 12, 31, 16, 0, 0, 0, -8 * 60 * 60, "PST"}}, + {1221681866, Time{2008, 9, 17, 13, 4, 26, 0, -7 * 60 * 60, "PDT"}}, } var nanolocaltests = []TimeTest{ - {0, Time{1969, 12, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}}, - {1221681866, Time{2008, 9, 17, 13, 4, 26, 3e8, Wednesday, -7 * 60 * 60, "PDT"}}, + {0, Time{1969, 12, 31, 16, 0, 0, 1e8, -8 * 60 * 60, "PST"}}, + {1221681866, Time{2008, 9, 17, 13, 4, 26, 3e8, -7 * 60 * 60, "PDT"}}, } func same(t, u *Time) bool { @@ -65,7 +65,7 @@ func same(t, u *Time) bool { t.Minute == u.Minute && t.Second == u.Second && t.Nanosecond == u.Nanosecond && - t.Weekday == u.Weekday && + t.Weekday() == u.Weekday() && t.ZoneOffset == u.ZoneOffset && t.Zone == u.Zone } @@ -173,9 +173,9 @@ type TimeFormatTest struct { } var rfc3339Formats = []TimeFormatTest{ - {Time{2008, 9, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}, "2008-09-17T20:04:26Z"}, - {Time{1994, 9, 17, 20, 4, 26, 0, Wednesday, -18000, "EST"}, "1994-09-17T20:04:26-05:00"}, - {Time{2000, 12, 26, 1, 15, 6, 0, Wednesday, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"}, + {Time{2008, 9, 17, 20, 4, 26, 0, 0, "UTC"}, "2008-09-17T20:04:26Z"}, + {Time{1994, 9, 17, 20, 4, 26, 0, -18000, "EST"}, "1994-09-17T20:04:26-05:00"}, + {Time{2000, 12, 26, 1, 15, 6, 0, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"}, } func TestRFC3339Conversion(t *testing.T) { @@ -323,8 +323,8 @@ func checkTime(time *Time, test *ParseTest, t *testing.T) { if test.hasTZ && time.ZoneOffset != -28800 { t.Errorf("%s: bad tz offset: %d not %d", test.name, time.ZoneOffset, -28800) } - if test.hasWD && time.Weekday != 4 { - t.Errorf("%s: bad weekday: %d not %d", test.name, time.Weekday, 4) + if test.hasWD && time.Weekday() != 4 { + t.Errorf("%s: bad weekday: %d not %d", test.name, time.Weekday(), 4) } } @@ -450,11 +450,11 @@ func Test12AMIsMidnight(t *testing.T) { // Check that a time without a Zone still produces a (numeric) time zone // when formatted with MST as a requested zone. func TestMissingZone(t *testing.T) { - time, err := Parse(RubyDate, "Tue Feb 02 16:10:03 -0500 2006") + time, err := Parse(RubyDate, "Thu Feb 02 16:10:03 -0500 2006") if err != nil { t.Fatal("error parsing date:", err) } - expect := "Tue Feb 2 16:10:03 -0500 2006" // -0500 not EST + expect := "Thu Feb 2 16:10:03 -0500 2006" // -0500 not EST str := time.Format(UnixDate) // uses MST as its time zone if str != expect { t.Errorf("expected %q got %q", expect, str) diff --git a/src/pkg/time/zoneinfo_posix.go b/src/pkg/time/zoneinfo_posix.go index b49216410..b0fa6c33b 100644 --- a/src/pkg/time/zoneinfo_posix.go +++ b/src/pkg/time/zoneinfo_posix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd plan9 + package time import "sync" diff --git a/src/pkg/time/zoneinfo_unix.go b/src/pkg/time/zoneinfo_unix.go index ce4d9f13a..0dc423531 100644 --- a/src/pkg/time/zoneinfo_unix.go +++ b/src/pkg/time/zoneinfo_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. // See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go index ab3e7df59..41c48192d 100644 --- a/src/pkg/time/zoneinfo_windows.go +++ b/src/pkg/time/zoneinfo_windows.go @@ -70,7 +70,15 @@ func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uin // Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format. func (z *zone) preCalculateAbsSec() { if z.year != 0 { - z.abssec = (&Time{z.year, int(z.month), int(z.day), int(z.hour), int(z.minute), int(z.second), 0, 0, 0, ""}).Seconds() + t := &Time{ + Year: z.year, + Month: int(z.month), + Day: int(z.day), + Hour: int(z.hour), + Minute: int(z.minute), + Second: int(z.second), + } + z.abssec = t.Seconds() // Time given is in "local" time. Adjust it for "utc". z.abssec -= int64(z.prev.offset) } @@ -83,9 +91,16 @@ func (z *zone) cutoffSeconds(year int64) int64 { // z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6) // z.day is week within the month (1 to 5, where 5 is last week of the month) // z.hour, z.minute and z.second are absolute time - t := &Time{year, int(z.month), 1, int(z.hour), int(z.minute), int(z.second), 0, 0, 0, ""} + t := &Time{ + Year: year, + Month: int(z.month), + Day: 1, + Hour: int(z.hour), + Minute: int(z.minute), + Second: int(z.second), + } t = SecondsToUTC(t.Seconds()) - i := int(z.dayofweek) - t.Weekday + i := int(z.dayofweek) - t.Weekday() if i < 0 { i += 7 } diff --git a/src/pkg/websocket/websocket.go b/src/pkg/websocket/websocket.go index 1d063c31f..d57d1149c 100644 --- a/src/pkg/websocket/websocket.go +++ b/src/pkg/websocket/websocket.go @@ -57,16 +57,13 @@ var ( ErrNotSupported = ProtocolError{"not supported"} ) -// WebSocketAddr is an implementation of net.Addr for WebSocket. -type WebSocketAddr struct { +// Addr is an implementation of net.Addr for WebSocket. +type Addr struct { *url.URL } // Network returns the network type for a WebSocket, "websocket". -func (addr WebSocketAddr) Network() string { return "websocket" } - -// String returns the network address for a WebSocket. -func (addr WebSocketAddr) String() string { return addr.String() } +func (addr *Addr) Network() string { return "websocket" } // Config is a WebSocket configuration type Config struct { @@ -222,18 +219,18 @@ func (ws *Conn) IsServerConn() bool { return ws.request != nil } // the WebSocket location for server. func (ws *Conn) LocalAddr() net.Addr { if ws.IsClientConn() { - return WebSocketAddr{ws.config.Origin} + return &Addr{ws.config.Origin} } - return WebSocketAddr{ws.config.Location} + return &Addr{ws.config.Location} } // RemoteAddr returns the WebSocket location for the connection for client, or // the Websocket Origin for server. func (ws *Conn) RemoteAddr() net.Addr { if ws.IsClientConn() { - return WebSocketAddr{ws.config.Location} + return &Addr{ws.config.Location} } - return WebSocketAddr{ws.config.Origin} + return &Addr{ws.config.Origin} } // SetTimeout sets the connection's network timeout in nanoseconds. diff --git a/src/pkg/websocket/websocket_test.go b/src/pkg/websocket/websocket_test.go index 058f38ce0..d2834cd1f 100644 --- a/src/pkg/websocket/websocket_test.go +++ b/src/pkg/websocket/websocket_test.go @@ -87,6 +87,31 @@ func TestEcho(t *testing.T) { conn.Close() } +func TestAddr(t *testing.T) { + once.Do(startServer) + + // websocket.Dial() + client, err := net.Dial("tcp", serverAddr) + if err != nil { + t.Fatal("dialing", err) + } + conn, err := NewClient(newConfig(t, "/echo"), client) + if err != nil { + t.Errorf("WebSocket handshake error: %v", err) + return + } + + ra := conn.RemoteAddr().String() + if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { + t.Errorf("Bad remote addr: %v", ra) + } + la := conn.LocalAddr().String() + if !strings.HasPrefix(la, "http://") { + t.Errorf("Bad local addr: %v", la) + } + conn.Close() +} + func TestCount(t *testing.T) { once.Do(startServer) |