github.com/mitranim/gg@v0.1.17/internal.go (about)

     1  package gg
     2  
     3  import (
     4  	"math"
     5  	"path/filepath"
     6  	r "reflect"
     7  	"regexp"
     8  	"strings"
     9  	u "unsafe"
    10  )
    11  
    12  func typeBitSize(typ r.Type) int { return int(typ.Size() * 8) }
    13  
    14  // Borrowed from the standard library. Requires caution.
    15  func noescape(src u.Pointer) u.Pointer {
    16  	out := uintptr(src)
    17  	//nolint:staticcheck
    18  	return u.Pointer(out ^ 0)
    19  }
    20  
    21  func errAppendInner(buf Buf, err error) Buf {
    22  	if err != nil {
    23  		buf.AppendString(`: `)
    24  		buf.AppendError(err)
    25  	}
    26  	return buf
    27  }
    28  
    29  func errAppendTraceIndent(buf Buf, trace Trace) Buf {
    30  	if trace.IsNotEmpty() {
    31  		buf.AppendNewline()
    32  		buf.AppendString(`trace:`)
    33  		buf = trace.AppendIndent(buf, 1)
    34  	}
    35  	return buf
    36  }
    37  
    38  func isFuncNameAnon(val string) bool {
    39  	const pre = `func`
    40  	return strings.HasPrefix(val, pre) && hasPrefixDigit(val[len(pre):])
    41  }
    42  
    43  func hasPrefixDigit(val string) bool { return isDigit(TextHeadByte(val)) }
    44  
    45  func isDigit(val byte) bool { return val >= '0' && val <= '9' }
    46  
    47  func validateLenMatch(one, two int) {
    48  	if one != two {
    49  		panic(Errf(
    50  			`unable to iterate pairwise: length mismatch: %v and %v`,
    51  			one, two,
    52  		))
    53  	}
    54  }
    55  
    56  // Note: `strconv.ParseBool` is too permissive for our taste.
    57  func parseBool(src string, out r.Value) error {
    58  	switch src {
    59  	case `true`:
    60  		out.SetBool(true)
    61  		return nil
    62  
    63  	case `false`:
    64  		out.SetBool(false)
    65  		return nil
    66  
    67  	default:
    68  		return ErrParse(ErrInvalidInput, src, Type[bool]())
    69  	}
    70  }
    71  
    72  /*
    73  Somewhat similar to `filepath.Rel`, but doesn't support `..`, performs
    74  significantly better, and returns the path as-is when it doesn't start
    75  with the given base.
    76  */
    77  func relOpt(base, src string) string {
    78  	if strings.HasPrefix(src, base) {
    79  		rem := src[len(base):]
    80  		if len(rem) > 0 && rem[0] == filepath.Separator {
    81  			return rem[1:]
    82  		}
    83  	}
    84  	return src
    85  }
    86  
    87  func isIntString(val string) bool {
    88  	if len(val) <= 0 {
    89  		return false
    90  	}
    91  
    92  	if len(val) > 0 && (val[0] == '+' || val[0] == '-') {
    93  		val = val[1:]
    94  	}
    95  
    96  	if len(val) <= 0 {
    97  		return false
    98  	}
    99  
   100  	// Note: here we iterate bytes rather than UTF-8 characters because digits
   101  	// are always single byte and we abort on the first mismatch. This may be
   102  	// slightly more efficient than iterating characters.
   103  	for ind := 0; ind < len(val); ind++ {
   104  		if !isDigit(val[ind]) {
   105  			return false
   106  		}
   107  	}
   108  	return true
   109  }
   110  
   111  func isCliFlag(val string) bool { return TextHeadByte(val) == '-' }
   112  
   113  func isCliFlagValid(val string) bool { return reCliFlag.Get().MatchString(val) }
   114  
   115  /*
   116  Must begin with `-` and consist of alphanumeric characters, optionally
   117  containing `-` between those characters.
   118  
   119  TODO test.
   120  */
   121  var reCliFlag = NewLazy(func() *regexp.Regexp {
   122  	return regexp.MustCompile(`^-+[\p{L}\d]+(?:[\p{L}\d-]*[\p{L}\d])?$`)
   123  })
   124  
   125  func cliFlagSplit(src string) (_ string, _ string, _ bool) {
   126  	if !isCliFlag(src) {
   127  		return
   128  	}
   129  
   130  	ind := strings.IndexRune(src, '=')
   131  	if ind >= 0 {
   132  		return src[:ind], src[ind+1:], true
   133  	}
   134  
   135  	return src, ``, false
   136  }
   137  
   138  /*
   139  Represents nodes in a linked list. Normally in Go, linked lists tend to be an
   140  anti-pattern; slices perform better in most scenarios, and don't require an
   141  additional abstraction. However, there is one valid scenario for linked lists:
   142  when nodes are pointers to local variables, when those local variables don't
   143  escape, and when they represent addresses to actual memory regions in stack
   144  frames. In this case, this may provide us with a resizable data structure
   145  allocated entirely on the stack, which is useful for book-keeping in recursive
   146  tree-walking or graph-walking algorithms. We currently do not verify if the
   147  trick has the expected efficiency, as the overheads are minimal.
   148  */
   149  type node[A comparable] struct {
   150  	tail *node[A]
   151  	val  A
   152  }
   153  
   154  func (self node[A]) has(val A) bool {
   155  	return self.val == val || (self.tail != nil && self.tail.has(val))
   156  }
   157  
   158  func (self *node[A]) cons(val A) (out node[A]) {
   159  	out.tail = self
   160  	out.val = val
   161  	return
   162  }
   163  
   164  /*
   165  Suboptimal: doesn't preallocate capacity. We only call this in case of errors,
   166  so the overhead should be negligible.
   167  */
   168  func (self node[A]) vals() (out []A) {
   169  	out = append(out, self.val)
   170  	node := self.tail
   171  	for node != nil {
   172  		out = append(out, node.val)
   173  		node = node.tail
   174  	}
   175  	return
   176  }
   177  
   178  func safeUintToInt(src uint) int {
   179  	if src > math.MaxInt {
   180  		return math.MaxInt
   181  	}
   182  	return int(src)
   183  }
   184  
   185  func isByteNewline(val byte) bool { return val == '\n' || val == '\r' }
   186  
   187  func errCollMissing[Val, Key any](key Key) Err {
   188  	return Errf(`missing value of type %v for key %v`, Type[Val](), key)
   189  }