| // Copyright (c) 2015, 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. |
| |
| package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "errors" |
| "fmt" |
| "io" |
| "os" |
| "path" |
| "sort" |
| "strconv" |
| ) |
| |
| // libraryNames must be kept in sync with the enum in err.h. The generated code |
| // will contain static assertions to enforce this. |
| var libraryNames = []string{ |
| "NONE", |
| "SYS", |
| "BN", |
| "RSA", |
| "DH", |
| "EVP", |
| "BUF", |
| "OBJ", |
| "PEM", |
| "DSA", |
| "X509", |
| "ASN1", |
| "CONF", |
| "CRYPTO", |
| "EC", |
| "SSL", |
| "BIO", |
| "PKCS7", |
| "PKCS8", |
| "X509V3", |
| "RAND", |
| "ENGINE", |
| "OCSP", |
| "UI", |
| "COMP", |
| "ECDSA", |
| "ECDH", |
| "HMAC", |
| "DIGEST", |
| "CIPHER", |
| "HKDF", |
| "TRUST_TOKEN", |
| "USER", |
| } |
| |
| // stringList is a map from uint32 -> string which can output data for a sorted |
| // list as C literals. |
| type stringList struct { |
| // entries is an array of keys and offsets into |stringData|. The |
| // offsets are in the bottom 15 bits of each uint32 and the key is the |
| // top 17 bits. |
| entries []uint32 |
| // internedStrings contains the same strings as are in |stringData|, |
| // but allows for easy deduplication. It maps a string to its offset in |
| // |stringData|. |
| internedStrings map[string]uint32 |
| stringData []byte |
| } |
| |
| func newStringList() *stringList { |
| return &stringList{ |
| internedStrings: make(map[string]uint32), |
| } |
| } |
| |
| // offsetMask is the bottom 15 bits. It's a mask that selects the offset from a |
| // uint32 in entries. |
| const offsetMask = 0x7fff |
| |
| func (st *stringList) Add(key uint32, value string) error { |
| if key&offsetMask != 0 { |
| return errors.New("need bottom 15 bits of the key for the offset") |
| } |
| offset, ok := st.internedStrings[value] |
| if !ok { |
| offset = uint32(len(st.stringData)) |
| if offset&offsetMask != offset { |
| return errors.New("stringList overflow") |
| } |
| st.stringData = append(st.stringData, []byte(value)...) |
| st.stringData = append(st.stringData, 0) |
| st.internedStrings[value] = offset |
| } |
| |
| for _, existing := range st.entries { |
| if existing>>15 == key>>15 { |
| panic("duplicate entry") |
| } |
| } |
| st.entries = append(st.entries, key|offset) |
| return nil |
| } |
| |
| func (st *stringList) buildList() []uint32 { |
| sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) }) |
| return st.entries |
| } |
| |
| type stringWriter interface { |
| io.Writer |
| WriteString(string) (int, error) |
| } |
| |
| func (st *stringList) WriteTo(out stringWriter, name string) { |
| list := st.buildList() |
| values := "kOpenSSL" + name + "Values" |
| out.WriteString("const uint32_t " + values + "[] = {\n") |
| for _, v := range list { |
| fmt.Fprintf(out, " 0x%x,\n", v) |
| } |
| out.WriteString("};\n\n") |
| out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n") |
| |
| stringData := "kOpenSSL" + name + "StringData" |
| out.WriteString("const char " + stringData + "[] =\n \"") |
| for i, c := range st.stringData { |
| if c == 0 { |
| out.WriteString("\\0\"\n \"") |
| continue |
| } |
| out.Write(st.stringData[i : i+1]) |
| } |
| out.WriteString("\";\n\n") |
| } |
| |
| type errorData struct { |
| reasons *stringList |
| libraryMap map[string]uint32 |
| } |
| |
| func (e *errorData) readErrorDataFile(filename string) error { |
| inFile, err := os.Open(filename) |
| if err != nil { |
| return err |
| } |
| defer inFile.Close() |
| |
| scanner := bufio.NewScanner(inFile) |
| comma := []byte(",") |
| |
| lineNo := 0 |
| for scanner.Scan() { |
| lineNo++ |
| |
| line := scanner.Bytes() |
| if len(line) == 0 { |
| continue |
| } |
| parts := bytes.Split(line, comma) |
| if len(parts) != 3 { |
| return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts)) |
| } |
| libNum, ok := e.libraryMap[string(parts[0])] |
| if !ok { |
| return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename) |
| } |
| if libNum >= 64 { |
| return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename) |
| } |
| key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */) |
| if err != nil { |
| return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err) |
| } |
| if key >= 2048 { |
| return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename) |
| } |
| value := string(parts[2]) |
| |
| listKey := libNum<<26 | uint32(key)<<15 |
| |
| err = e.reasons.Add(listKey, value) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return scanner.Err() |
| } |
| |
| type ErrDataTask struct { |
| TargetName string |
| Inputs []string |
| } |
| |
| func (t *ErrDataTask) Destination() string { |
| return path.Join("gen", t.TargetName, "err_data.c") |
| } |
| |
| func (t *ErrDataTask) Run() ([]byte, error) { |
| e := &errorData{ |
| reasons: newStringList(), |
| libraryMap: make(map[string]uint32), |
| } |
| for i, name := range libraryNames { |
| e.libraryMap[name] = uint32(i) + 1 |
| } |
| |
| for _, input := range t.Inputs { |
| if err := e.readErrorDataFile(input); err != nil { |
| return nil, err |
| } |
| } |
| |
| var out bytes.Buffer |
| out.WriteString(`/* Copyright (c) 2015, 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 was generated by go run ./util/pregenerate. */ |
| |
| #include <openssl/base.h> |
| #include <openssl/err.h> |
| |
| #include <assert.h> |
| |
| `) |
| |
| for i, name := range libraryNames { |
| fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1) |
| } |
| fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1) |
| out.WriteString("\n") |
| |
| e.reasons.WriteTo(&out, "Reason") |
| return out.Bytes(), nil |
| } |