// Copyright (C) 2024 Umorpha Systems // SPDX-License-Identifier: AGPL-3.0-or-later package gitcache import ( "strings" ) func needsEscaped(b byte) bool { switch { case b == '!', // our own escape // see: git-check-ref-format(1) b <= ' ', b == '\x7F', // never: ASCII control characters b == '~', b == '^', b == ':', // never: gitrevision meaning b == '?', b == '*', b == '[', // never: refspec pattern b == '\\', // never: backslash b == '.', b == '@', b == '/': // sometimes: special rules return true default: return false } } func URL2NS(url string) (string, bool) { url, ok := NormalizeURL(url) if !ok { return "", false } l := len(url) for i := 0; i < len(url); i++ { if needsEscaped(url[i]) { l += 2 } } var ns strings.Builder ns.Grow(l) var buf [3]byte buf[0] = '!' const digits = "0123456789abcdef" for i := 0; i < len(url); i++ { b := url[i] if needsEscaped(b) { buf[1] = digits[b>>4] buf[2] = digits[b&0xf] ns.Write(buf[:]) } else { ns.WriteByte(b) } } return ns.String(), true } func hex2int(h byte) (byte, bool) { switch { case '0' <= h && h <= '9': return h - '0', true case 'a' <= h && h <= 'f': return h - 'a' + 0xa, true default: return 0, false } } func NS2URL(ns string) (string, bool) { l := len(ns) - (strings.Count(ns, "!") * 2) if l < 0 { return "", false } var url strings.Builder url.Grow(l) for i := 0; i < len(ns); i++ { b := ns[i] if b == '!' { if i+2 >= len(ns) { return "", false } h, hOK := hex2int(ns[i+1]) l, lOK := hex2int(ns[i+2]) if !hOK || !lOK { return "", false } b = h<<4 | l i += 2 } url.WriteByte(b) } return url.String(), true }