| // Copyright (c) 2017, Google Inc. | |
| // | |
| // Permission to use, copy, modify, and/or distribute this software for any | |
| // purpose with or without fee is hereby granted, provided that the above | |
| // copyright notice and this permission notice appear in all copies. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | |
| // embed_test_data generates a C++ source file which exports a function, | |
| // GetTestData, which looks up the specified data files. | |
| package main | |
| import ( | |
| "bytes" | |
| "flag" | |
| "fmt" | |
| "io/ioutil" | |
| "os" | |
| "strings" | |
| ) | |
| var 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") | |
| func quote(in []byte) string { | |
| var lastWasHex bool | |
| var buf bytes.Buffer | |
| buf.WriteByte('"') | |
| for _, b := range in { | |
| var wasHex bool | |
| switch b { | |
| case '\a': | |
| buf.WriteString(`\a`) | |
| case '\b': | |
| buf.WriteString(`\b`) | |
| case '\f': | |
| buf.WriteString(`\f`) | |
| case '\n': | |
| buf.WriteString(`\n`) | |
| case '\r': | |
| buf.WriteString(`\r`) | |
| case '\t': | |
| buf.WriteString(`\t`) | |
| case '\v': | |
| buf.WriteString(`\v`) | |
| case '"': | |
| buf.WriteString(`\"`) | |
| case '\\': | |
| buf.WriteString(`\\`) | |
| default: | |
| // Emit printable ASCII characters, [32, 126], as-is to minimize | |
| // file size. However, if the previous character used a hex escape | |
| // sequence, do not emit 0-9 and a-f as-is. C++ interprets "\x123" | |
| // as a single (overflowing) escape sequence, rather than '\x12' | |
| // followed by '3'. | |
| isHexDigit := ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F') | |
| if 32 <= b && b <= 126 && !(lastWasHex && isHexDigit) { | |
| buf.WriteByte(b) | |
| } else { | |
| fmt.Fprintf(&buf, "\\x%02x", b) | |
| wasHex = true | |
| } | |
| } | |
| lastWasHex = wasHex | |
| } | |
| buf.WriteByte('"') | |
| return buf.String() | |
| } | |
| func main() { | |
| flag.Parse() | |
| var files []string | |
| if len(*fileList) != 0 { | |
| data, err := ioutil.ReadFile(*fileList) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", *fileList, err) | |
| os.Exit(1) | |
| } | |
| files = strings.FieldsFunc(string(data), func(r rune) bool { return r == '\r' || r == '\n' }) | |
| } | |
| files = append(files, flag.Args()...) | |
| fmt.Printf(`/* Copyright (c) 2017, Google Inc. | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | |
| /* This file is generated by: | |
| `) | |
| fmt.Printf(" * go run util/embed_test_data.go") | |
| for _, arg := range files { | |
| fmt.Printf(" \\\n * %s", arg) | |
| } | |
| fmt.Printf(" */\n") | |
| fmt.Printf(` | |
| /* clang-format off */ | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <string> | |
| `) | |
| // MSVC limits the length of string constants, so we emit an array of | |
| // them and concatenate at runtime. We could also use a single array | |
| // literal, but this is less compact. | |
| const chunkSize = 8192 | |
| for i, arg := range files { | |
| data, err := ioutil.ReadFile(arg) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", arg, err) | |
| os.Exit(1) | |
| } | |
| fmt.Printf("static const size_t kLen%d = %d;\n\n", i, len(data)) | |
| fmt.Printf("static const char *kData%d[] = {\n", i) | |
| for len(data) > 0 { | |
| chunk := chunkSize | |
| if chunk > len(data) { | |
| chunk = len(data) | |
| } | |
| fmt.Printf(" %s,\n", quote(data[:chunk])) | |
| data = data[chunk:] | |
| } | |
| fmt.Printf("};\n") | |
| } | |
| fmt.Printf(`static std::string AssembleString(const char **data, size_t len) { | |
| std::string ret; | |
| for (size_t i = 0; i < len; i += %d) { | |
| size_t chunk = std::min(static_cast<size_t>(%d), len - i); | |
| ret.append(data[i / %d], chunk); | |
| } | |
| return ret; | |
| } | |
| /* Silence -Wmissing-declarations. */ | |
| std::string GetTestData(const char *path); | |
| std::string GetTestData(const char *path) { | |
| `, chunkSize, chunkSize, chunkSize) | |
| for i, arg := range files { | |
| fmt.Printf(" if (strcmp(path, %s) == 0) {\n", quote([]byte(arg))) | |
| fmt.Printf(" return AssembleString(kData%d, kLen%d);\n", i, i) | |
| fmt.Printf(" }\n") | |
| } | |
| fmt.Printf(` fprintf(stderr, "File not embedded: %%s.\n", path); | |
| abort(); | |
| } | |
| `) | |
| } |