Black Lives Matter. Support the Equal Justice Initiative.

Source file src/strconv/makeisprint.go

Documentation: strconv

     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  //go:build ignore
     6  // +build ignore
     7  
     8  //
     9  // usage:
    10  //
    11  // go run makeisprint.go -output isprint.go
    12  //
    13  
    14  package main
    15  
    16  import (
    17  	"bytes"
    18  	"flag"
    19  	"fmt"
    20  	"go/format"
    21  	"log"
    22  	"os"
    23  	"unicode"
    24  )
    25  
    26  var filename = flag.String("output", "isprint.go", "output file name")
    27  
    28  var (
    29  	range16  []uint16
    30  	except16 []uint16
    31  	range32  []uint32
    32  	except32 []uint32
    33  )
    34  
    35  // bsearch16 returns the smallest i such that a[i] >= x.
    36  // If there is no such i, bsearch16 returns len(a).
    37  func bsearch16(a []uint16, x uint16) int {
    38  	i, j := 0, len(a)
    39  	for i < j {
    40  		h := i + (j-i)>>1
    41  		if a[h] < x {
    42  			i = h + 1
    43  		} else {
    44  			j = h
    45  		}
    46  	}
    47  	return i
    48  }
    49  
    50  // bsearch32 returns the smallest i such that a[i] >= x.
    51  // If there is no such i, bsearch32 returns len(a).
    52  func bsearch32(a []uint32, x uint32) int {
    53  	i, j := 0, len(a)
    54  	for i < j {
    55  		h := i + (j-i)>>1
    56  		if a[h] < x {
    57  			i = h + 1
    58  		} else {
    59  			j = h
    60  		}
    61  	}
    62  	return i
    63  }
    64  
    65  func isPrint(r rune) bool {
    66  	// Same algorithm, either on uint16 or uint32 value.
    67  	// First, find first i such that rang[i] >= x.
    68  	// This is the index of either the start or end of a pair that might span x.
    69  	// The start is even (rang[i&^1]) and the end is odd (rang[i|1]).
    70  	// If we find x in a range, make sure x is not in exception list.
    71  
    72  	if 0 <= r && r < 1<<16 {
    73  		rr, rang, except := uint16(r), range16, except16
    74  		i := bsearch16(rang, rr)
    75  		if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
    76  			return false
    77  		}
    78  		j := bsearch16(except, rr)
    79  		return j >= len(except) || except[j] != rr
    80  	}
    81  
    82  	rr, rang, except := uint32(r), range32, except32
    83  	i := bsearch32(rang, rr)
    84  	if i >= len(rang) || rr < rang[i&^1] || rang[i|1] < rr {
    85  		return false
    86  	}
    87  	j := bsearch32(except, rr)
    88  	return j >= len(except) || except[j] != rr
    89  }
    90  
    91  func scan(min, max rune) (rang, except []uint32) {
    92  	lo := rune(-1)
    93  	for i := min; ; i++ {
    94  		if (i > max || !unicode.IsPrint(i)) && lo >= 0 {
    95  			// End range, but avoid flip flop.
    96  			if i+1 <= max && unicode.IsPrint(i+1) {
    97  				except = append(except, uint32(i))
    98  				continue
    99  			}
   100  			rang = append(rang, uint32(lo), uint32(i-1))
   101  			lo = -1
   102  		}
   103  		if i > max {
   104  			break
   105  		}
   106  		if lo < 0 && unicode.IsPrint(i) {
   107  			lo = i
   108  		}
   109  	}
   110  	return
   111  }
   112  
   113  func to16(x []uint32) []uint16 {
   114  	var y []uint16
   115  	for _, v := range x {
   116  		if uint32(uint16(v)) != v {
   117  			panic("bad 32->16 conversion")
   118  		}
   119  		y = append(y, uint16(v))
   120  	}
   121  	return y
   122  }
   123  
   124  func main() {
   125  	flag.Parse()
   126  
   127  	rang, except := scan(0, 0xFFFF)
   128  	range16 = to16(rang)
   129  	except16 = to16(except)
   130  	range32, except32 = scan(0x10000, unicode.MaxRune)
   131  
   132  	for i := rune(0); i <= unicode.MaxRune; i++ {
   133  		if isPrint(i) != unicode.IsPrint(i) {
   134  			log.Fatalf("%U: isPrint=%v, want %v\n", i, isPrint(i), unicode.IsPrint(i))
   135  		}
   136  	}
   137  
   138  	var buf bytes.Buffer
   139  
   140  	fmt.Fprintf(&buf, `// Copyright 2013 The Go Authors. All rights reserved.
   141  // Use of this source code is governed by a BSD-style
   142  // license that can be found in the LICENSE file.`+"\n\n")
   143  	fmt.Fprintf(&buf, "// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT.\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 = os.WriteFile(*filename, data, 0644)
   201  	if err != nil {
   202  		log.Fatal(err)
   203  	}
   204  }
   205  

View as plain text