1// Copyright (c) 2017, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15// embed_test_data generates a C++ source file which exports a function, 16// GetTestData, which looks up the specified data files. 17package main 18 19import ( 20 "bytes" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "strings" 26) 27 28var fileList = flag.String("file-list", "", "if not empty, the path to a file containing a newline-separated list of files, to work around Windows command-line limits") 29 30func quote(in []byte) string { 31 var lastWasHex bool 32 var buf bytes.Buffer 33 buf.WriteByte('"') 34 for _, b := range in { 35 var wasHex bool 36 switch b { 37 case '\a': 38 buf.WriteString(`\a`) 39 case '\b': 40 buf.WriteString(`\b`) 41 case '\f': 42 buf.WriteString(`\f`) 43 case '\n': 44 buf.WriteString(`\n`) 45 case '\r': 46 buf.WriteString(`\r`) 47 case '\t': 48 buf.WriteString(`\t`) 49 case '\v': 50 buf.WriteString(`\v`) 51 case '"': 52 buf.WriteString(`\"`) 53 case '\\': 54 buf.WriteString(`\\`) 55 default: 56 // Emit printable ASCII characters, [32, 126], as-is to minimize 57 // file size. However, if the previous character used a hex escape 58 // sequence, do not emit 0-9 and a-f as-is. C++ interprets "\x123" 59 // as a single (overflowing) escape sequence, rather than '\x12' 60 // followed by '3'. 61 isHexDigit := ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F') 62 if 32 <= b && b <= 126 && !(lastWasHex && isHexDigit) { 63 buf.WriteByte(b) 64 } else { 65 fmt.Fprintf(&buf, "\\x%02x", b) 66 wasHex = true 67 } 68 } 69 lastWasHex = wasHex 70 } 71 buf.WriteByte('"') 72 return buf.String() 73} 74 75func main() { 76 flag.Parse() 77 78 var files []string 79 if len(*fileList) != 0 { 80 data, err := ioutil.ReadFile(*fileList) 81 if err != nil { 82 fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", *fileList, err) 83 os.Exit(1) 84 } 85 files = strings.FieldsFunc(string(data), func(r rune) bool { return r == '\r' || r == '\n' }) 86 } 87 88 files = append(files, flag.Args()...) 89 90 fmt.Printf(`/* Copyright (c) 2017, Google Inc. 91 * 92 * Permission to use, copy, modify, and/or distribute this software for any 93 * purpose with or without fee is hereby granted, provided that the above 94 * copyright notice and this permission notice appear in all copies. 95 * 96 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 97 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 98 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 99 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 100 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 101 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 102 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 103 104/* This file is generated by: 105`) 106 fmt.Printf(" * go run util/embed_test_data.go") 107 for _, arg := range files { 108 fmt.Printf(" \\\n * %s", arg) 109 } 110 fmt.Printf(" */\n") 111 112 fmt.Printf(` 113/* clang-format off */ 114 115#include <stdlib.h> 116#include <string.h> 117 118#include <algorithm> 119#include <string> 120 121 122`) 123 124 // MSVC limits the length of string constants, so we emit an array of 125 // them and concatenate at runtime. We could also use a single array 126 // literal, but this is less compact. 127 const chunkSize = 8192 128 129 for i, arg := range files { 130 data, err := ioutil.ReadFile(arg) 131 if err != nil { 132 fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", arg, err) 133 os.Exit(1) 134 } 135 fmt.Printf("static const size_t kLen%d = %d;\n\n", i, len(data)) 136 137 fmt.Printf("static const char *kData%d[] = {\n", i) 138 for len(data) > 0 { 139 chunk := chunkSize 140 if chunk > len(data) { 141 chunk = len(data) 142 } 143 fmt.Printf(" %s,\n", quote(data[:chunk])) 144 data = data[chunk:] 145 } 146 fmt.Printf("};\n") 147 } 148 149 fmt.Printf(`static std::string AssembleString(const char **data, size_t len) { 150 std::string ret; 151 for (size_t i = 0; i < len; i += %d) { 152 size_t chunk = std::min(static_cast<size_t>(%d), len - i); 153 ret.append(data[i / %d], chunk); 154 } 155 return ret; 156} 157 158/* Silence -Wmissing-declarations. */ 159std::string GetTestData(const char *path); 160 161std::string GetTestData(const char *path) { 162`, chunkSize, chunkSize, chunkSize) 163 for i, arg := range files { 164 fmt.Printf(" if (strcmp(path, %s) == 0) {\n", quote([]byte(arg))) 165 fmt.Printf(" return AssembleString(kData%d, kLen%d);\n", i, i) 166 fmt.Printf(" }\n") 167 } 168 fmt.Printf(` fprintf(stderr, "File not embedded: %%s.\n", path); 169 abort(); 170} 171`) 172 173} 174