Source file src/strconv/makeisprint.go

     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  
     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  	"log"
    21  	"os"
    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)>>1
    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)>>1
    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, "// Code generated by go run makeisprint.go -output isprint.go; DO NOT EDIT.\n\n")
   143  	fmt.Fprintf(&buf, "package strconv\n\n")
   144  
   145  	fmt.Fprintf(&buf, "// (%d+%d+%d)*2 + (%d)*4 = %d bytes\n\n",
   146  		len(range16), len(except16), len(except32),
   147  		len(range32),
   148  		(len(range16)+len(except16)+len(except32))*2+
   149  			(len(range32))*4)
   150  
   151  	fmt.Fprintf(&buf, "var isPrint16 = []uint16{\n")
   152  	for i := 0; i < len(range16); i += 2 {
   153  		fmt.Fprintf(&buf, "\t%#04x, %#04x,\n", range16[i], range16[i+1])
   154  	}
   155  	fmt.Fprintf(&buf, "}\n\n")
   156  
   157  	fmt.Fprintf(&buf, "var isNotPrint16 = []uint16{\n")
   158  	for _, r := range except16 {
   159  		fmt.Fprintf(&buf, "\t%#04x,\n", r)
   160  	}
   161  	fmt.Fprintf(&buf, "}\n\n")
   162  
   163  	fmt.Fprintf(&buf, "var isPrint32 = []uint32{\n")
   164  	for i := 0; i < len(range32); i += 2 {
   165  		fmt.Fprintf(&buf, "\t%#06x, %#06x,\n", range32[i], range32[i+1])
   166  	}
   167  	fmt.Fprintf(&buf, "}\n\n")
   168  
   169  	fmt.Fprintf(&buf, "var isNotPrint32 = []uint16{ // add 0x10000 to each entry\n")
   170  	for _, r := range except32 {
   171  		if r >= 0x20000 {
   172  			log.Fatalf("%U too big for isNotPrint32\n", r)
   173  		}
   174  		fmt.Fprintf(&buf, "\t%#04x,\n", r-0x10000)
   175  	}
   176  	fmt.Fprintf(&buf, "}\n\n")
   177  
   178  	// The list of graphic but not "printable" runes is short. Just make one easy table.
   179  	fmt.Fprintf(&buf, "// isGraphic lists the graphic runes not matched by IsPrint.\n")
   180  	fmt.Fprintf(&buf, "var isGraphic = []uint16{\n")
   181  	for r := rune(0); r <= unicode.MaxRune; r++ {
   182  		if unicode.IsPrint(r) != unicode.IsGraphic(r) {
   183  			// Sanity check.
   184  			if !unicode.IsGraphic(r) {
   185  				log.Fatalf("%U is printable but not graphic\n", r)
   186  			}
   187  			if r > 0xFFFF { // We expect only 16-bit values.
   188  				log.Fatalf("%U too big for isGraphic\n", r)
   189  			}
   190  			fmt.Fprintf(&buf, "\t%#04x,\n", r)
   191  		}
   192  	}
   193  	fmt.Fprintf(&buf, "}\n")
   194  
   195  	data, err := format.Source(buf.Bytes())
   196  	if err != nil {
   197  		log.Fatal(err)
   198  	}
   199  	err = os.WriteFile(*filename, data, 0644)
   200  	if err != nil {
   201  		log.Fatal(err)
   202  	}
   203  }
   204  

View as plain text