| // Copyright 2016 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. |
| |
| // +build ignore |
| |
| package main |
| |
| import ( |
| "log" |
| "strings" |
| "unicode" |
| |
| "golang.org/x/text/internal/gen" |
| "golang.org/x/text/internal/ucd" |
| ) |
| |
| // snippet is a slice of data; data is the concatenation of all of the names. |
| type snippet struct { |
| offset int |
| length int |
| s string |
| } |
| |
| func makeTable0EntryDirect(rOffset, rLength, dOffset, dLength int) uint64 { |
| if rOffset >= 1<<bitsRuneOffset { |
| log.Fatalf("makeTable0EntryDirect: rOffset %d is too large", rOffset) |
| } |
| if rLength >= 1<<bitsRuneLength { |
| log.Fatalf("makeTable0EntryDirect: rLength %d is too large", rLength) |
| } |
| if dOffset >= 1<<bitsDataOffset { |
| log.Fatalf("makeTable0EntryDirect: dOffset %d is too large", dOffset) |
| } |
| if dLength >= 1<<bitsRuneLength { |
| log.Fatalf("makeTable0EntryDirect: dLength %d is too large", dLength) |
| } |
| return uint64(rOffset)<<shiftRuneOffset | |
| uint64(rLength)<<shiftRuneLength | |
| uint64(dOffset)<<shiftDataOffset | |
| uint64(dLength)<<shiftDataLength | |
| 1 // Direct bit. |
| } |
| |
| func makeTable0EntryIndirect(rOffset, rLength, dBase, t1Offset int) uint64 { |
| if rOffset >= 1<<bitsRuneOffset { |
| log.Fatalf("makeTable0EntryIndirect: rOffset %d is too large", rOffset) |
| } |
| if rLength >= 1<<bitsRuneLength { |
| log.Fatalf("makeTable0EntryIndirect: rLength %d is too large", rLength) |
| } |
| if dBase >= 1<<bitsDataBase { |
| log.Fatalf("makeTable0EntryIndirect: dBase %d is too large", dBase) |
| } |
| if t1Offset >= 1<<bitsTable1Offset { |
| log.Fatalf("makeTable0EntryIndirect: t1Offset %d is too large", t1Offset) |
| } |
| return uint64(rOffset)<<shiftRuneOffset | |
| uint64(rLength)<<shiftRuneLength | |
| uint64(dBase)<<shiftDataBase | |
| uint64(t1Offset)<<shiftTable1Offset | |
| 0 // Direct bit. |
| } |
| |
| func makeTable1Entry(x int) uint16 { |
| if x < 0 || 0xffff < x { |
| log.Fatalf("makeTable1Entry: entry %d is out of range", x) |
| } |
| return uint16(x) |
| } |
| |
| var ( |
| data []byte |
| snippets = make([]snippet, 1+unicode.MaxRune) |
| ) |
| |
| func main() { |
| gen.Init() |
| |
| names, counts := parse() |
| appendRepeatNames(names, counts) |
| appendUniqueNames(names, counts) |
| |
| table0, table1 := makeTables() |
| |
| gen.Repackage("gen_bits.go", "bits.go", "runenames") |
| |
| w := gen.NewCodeWriter() |
| w.WriteVar("table0", table0) |
| w.WriteVar("table1", table1) |
| w.WriteConst("data", string(data)) |
| w.WriteGoFile("tables.go", "runenames") |
| } |
| |
| func parse() (names []string, counts map[string]int) { |
| names = make([]string, 1+unicode.MaxRune) |
| counts = map[string]int{} |
| ucd.Parse(gen.OpenUCDFile("UnicodeData.txt"), func(p *ucd.Parser) { |
| r, s := p.Rune(0), p.String(ucd.Name) |
| if s == "" { |
| return |
| } |
| if s[0] == '<' { |
| const first = ", First>" |
| if i := strings.Index(s, first); i >= 0 { |
| s = s[:i] + ">" |
| } |
| } |
| names[r] = s |
| counts[s]++ |
| }) |
| return names, counts |
| } |
| |
| func appendRepeatNames(names []string, counts map[string]int) { |
| alreadySeen := map[string]snippet{} |
| for r, s := range names { |
| if s == "" || counts[s] == 1 { |
| continue |
| } |
| if s[0] != '<' { |
| log.Fatalf("Repeated name %q does not start with a '<'", s) |
| } |
| |
| if z, ok := alreadySeen[s]; ok { |
| snippets[r] = z |
| continue |
| } |
| |
| z := snippet{ |
| offset: len(data), |
| length: len(s), |
| s: s, |
| } |
| data = append(data, s...) |
| snippets[r] = z |
| alreadySeen[s] = z |
| } |
| } |
| |
| func appendUniqueNames(names []string, counts map[string]int) { |
| for r, s := range names { |
| if s == "" || counts[s] != 1 { |
| continue |
| } |
| if s[0] == '<' { |
| log.Fatalf("Unique name %q starts with a '<'", s) |
| } |
| |
| z := snippet{ |
| offset: len(data), |
| length: len(s), |
| s: s, |
| } |
| data = append(data, s...) |
| snippets[r] = z |
| } |
| } |
| |
| func makeTables() (table0 []uint64, table1 []uint16) { |
| for i := 0; i < len(snippets); { |
| zi := snippets[i] |
| if zi == (snippet{}) { |
| i++ |
| continue |
| } |
| |
| // Look for repeat names. If we have one, we only need a table0 entry. |
| j := i + 1 |
| for ; j < len(snippets) && zi == snippets[j]; j++ { |
| } |
| if j > i+1 { |
| table0 = append(table0, makeTable0EntryDirect(i, j-i, zi.offset, zi.length)) |
| i = j |
| continue |
| } |
| |
| // Otherwise, we have a run of unique names. We need one table0 entry |
| // and two or more table1 entries. |
| base := zi.offset &^ (1<<dataBaseUnit - 1) |
| t1Offset := len(table1) + 1 |
| table1 = append(table1, makeTable1Entry(zi.offset-base)) |
| table1 = append(table1, makeTable1Entry(zi.offset+zi.length-base)) |
| for ; j < len(snippets) && snippets[j] != (snippet{}); j++ { |
| zj := snippets[j] |
| if data[zj.offset] == '<' { |
| break |
| } |
| table1 = append(table1, makeTable1Entry(zj.offset+zj.length-base)) |
| } |
| table0 = append(table0, makeTable0EntryIndirect(i, j-i, base>>dataBaseUnit, t1Offset)) |
| i = j |
| } |
| return table0, table1 |
| } |