1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build ignore 6 7 // 8 // usage: 9 // 10 // go run makeisprint.go -output isprint.go 11 // 12 13 package main 14 15 import ( 16 "bytes" 17 "flag" 18 "fmt" 19 "go/format" 20 "io/ioutil" 21 "log" 22 "unicode" 23 ) 24 25 var filename = flag.String("output", "isprint.go", "output file name") 26 27 var ( 28 range16 []uint16 29 except16 []uint16 30 range32 []uint32 31 except32 []uint32 32 ) 33 34 // bsearch16 returns the smallest i such that a[i] >= x. 35 // If there is no such i, bsearch16 returns len(a). 36 func bsearch16(a []uint16, x uint16) int { 37 i, j := 0, len(a) 38 for i < j { 39 h := i + (j-i)/2 40 if a[h] < x { 41 i = h + 1 42 } else { 43 j = h 44 } 45 } 46 return i 47 } 48 49 // bsearch32 returns the smallest i such that a[i] >= x. 50 // If there is no such i, bsearch32 returns len(a). 51 func bsearch32(a []uint32, x uint32) int { 52 i, j := 0, len(a) 53 for i < j { 54 h := i + (j-i)/2 55 if a[h] < x { 56 i = h + 1 57 } else { 58 j = h 59 } 60 } 61 return i 62 } 63 64 func isPrint(r rune) bool { 65 // Same algorithm, either on uint16 or uint32 value. 66 // First, find first i such that rang[i] >= x. 67 // This is the index of either the start or end of a pair that might span x. 68 // The start is even (rang[i&^1]) and the end is odd (rang[i|1]). 69 // If we find x in a range, make sure x is not in exception list. 70 71 if 0 <= r && r < 1<<16 { 72 rr, rang, except := uint16(r), range16, except16 73 i := bsearch16(rang, rr) 74 if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr { 75 return false 76 } 77 j := bsearch16(except, rr) 78 return j >= len(except) || except[j] != rr 79 } 80 81 rr, rang, except := uint32(r), range32, except32 82 i := bsearch32(rang, rr) 83 if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr { 84 return false 85 } 86 j := bsearch32(except, rr) 87 return j >= len(except) || except[j] != rr 88 } 89 90 func scan(min, max rune) (rang, except []uint32) { 91 lo := rune(-1) 92 for i := min; ; i++ { 93 if (i > max || !unicode.IsPrint(i)) && lo >= 0 { 94 // End range, but avoid flip flop. 95 if i+1 <= max && unicode.IsPrint(i+1) { 96 except = append(except, uint32(i)) 97 continue 98 } 99 rang = append(rang, uint32(lo), uint32(i-1)) 100 lo = -1 101 } 102 if i > max { 103 break 104 } 105 if lo < 0 && unicode.IsPrint(i) { 106 lo = i 107 } 108 } 109 return 110 } 111 112 func to16(x []uint32) []uint16 { 113 var y []uint16 114 for _, v := range x { 115 if uint32(uint16(v)) != v { 116 panic("bad 32->16 conversion") 117 } 118 y = append(y, uint16(v)) 119 } 120 return y 121 } 122 123 func main() { 124 flag.Parse() 125 126 rang, except := scan(0, 0xFFFF) 127 range16 = to16(rang) 128 except16 = to16(except) 129 range32, except32 = scan(0x10000, unicode.MaxRune) 130 131 for i := rune(0); i <= unicode.MaxRune; i++ { 132 if isPrint(i) != unicode.IsPrint(i) { 133 log.Fatalf("%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i)) 134 } 135 } 136 137 var buf bytes.Buffer 138 139 fmt.Fprintf(&buf, `// Copyright 2013 The Go Authors. All rights reserved. 140 // Use of this source code is governed by a BSD-style 141 // license that can be found in the LICENSE file.`+"\n\n") 142 fmt.Fprintf(&buf, "// DO NOT EDIT. GENERATED BY\n") 143 fmt.Fprintf(&buf, "// go run makeisprint.go -output isprint.go\n\n") 144 fmt.Fprintf(&buf, "package strconv\n\n") 145 146 fmt.Fprintf(&buf, "// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n", 147 len(range16), len(except16), len(except32), 148 len(range32), 149 (len(range16)+len(except16)+len(except32))*2+ 150 (len(range32))*4) 151 152 fmt.Fprintf(&buf, "var isPrint16 = []uint16{\n") 153 for i := 0; i < len(range16); i += 2 { 154 fmt.Fprintf(&buf, "\t%#04x, %#04x,\n", range16[i], range16[i+1]) 155 } 156 fmt.Fprintf(&buf, "}\n\n") 157 158 fmt.Fprintf(&buf, "var isNotPrint16 = []uint16{\n") 159 for _, r := range except16 { 160 fmt.Fprintf(&buf, "\t%#04x,\n", r) 161 } 162 fmt.Fprintf(&buf, "}\n\n") 163 164 fmt.Fprintf(&buf, "var isPrint32 = []uint32{\n") 165 for i := 0; i < len(range32); i += 2 { 166 fmt.Fprintf(&buf, "\t%#06x, %#06x,\n", range32[i], range32[i+1]) 167 } 168 fmt.Fprintf(&buf, "}\n\n") 169 170 fmt.Fprintf(&buf, "var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n") 171 for _, r := range except32 { 172 if r >= 0x20000 { 173 log.Fatalf("%U too big for isNotPrint32\n", r) 174 } 175 fmt.Fprintf(&buf, "\t%#04x,\n", r-0x10000) 176 } 177 fmt.Fprintf(&buf, "}\n\n") 178 179 // The list of graphic but not "printable" runes is short. Just make one easy table. 180 fmt.Fprintf(&buf, "// isGraphic lists the graphic runes not matched by IsPrint.\n") 181 fmt.Fprintf(&buf, "var isGraphic = []uint16{\n") 182 for r := rune(0); r <= unicode.MaxRune; r++ { 183 if unicode.IsPrint(r) != unicode.IsGraphic(r) { 184 // Sanity check. 185 if !unicode.IsGraphic(r) { 186 log.Fatalf("%U is printable but not graphic\n", r) 187 } 188 if r > 0xFFFF { // We expect only 16-bit values. 189 log.Fatalf("%U too big for isGraphic\n", r) 190 } 191 fmt.Fprintf(&buf, "\t%#04x,\n", r) 192 } 193 } 194 fmt.Fprintf(&buf, "}\n") 195 196 data, err := format.Source(buf.Bytes()) 197 if err != nil { 198 log.Fatal(err) 199 } 200 err = ioutil.WriteFile(*filename, data, 0644) 201 if err != nil { 202 log.Fatal(err) 203 } 204 } 205