Documentation
¶
Overview ¶
Package canon defines a canonical, engine-independent encoding for the result of evaluating a ClassAd attribute. Both evaluation engines under test (the native Go implementation and the reference C++ libclassad, reached via cgo) encode their results into this format so the differential fuzzer can compare them without being fooled by cosmetic formatting differences (e.g. Go prints reals as "0.5" while libclassad prints "5.000...E-01", and lists as "[1 2 3]" vs "{ 1,2,3 }").
The encoding is a single self-delimiting line. Strings and composite values are length/count prefixed so no escaping is required and parsing is unambiguous:
U undefined
E error
B0 | B1 boolean
I<int64>; integer (decimal)
R<%.17g>; real (round-trippable double)
G<%.17g>; relative time, seconds
A<%.17g>,<offset>; absolute time: epoch seconds, tz offset seconds
S<len>,<bytes> string: byte length then raw bytes
L<count>,<elem>... list: element count then encoded elements
C<count>,<klen>,<key><val>... classad: entry count then (keylen,key,value),
entries sorted by attribute name
The C++ shim (fuzz/oracle/cgo/shim.cc) emits byte-identical output for the scalar cases, so the common path is a plain string compare. When strings differ, callers re-parse both sides into a Value tree and compare with a float tolerance to suppress last-ULP noise from transcendental functions.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultTolerance = FloatTolerance{Rel: 1e-12, Abs: 1e-12}
DefaultTolerance is intentionally tight: we want to catch real arithmetic divergences, only forgiving sub-ULP rounding.
Functions ¶
func Describe ¶
Describe renders a value as a short human-readable string for divergence reports (distinct from the wire Encode format, which is for machines).
func Equal ¶
func Equal(a, b Value, tol FloatTolerance) bool
Equal reports whether two canonical values are semantically equal under the given float tolerance. Type (Kind) must match exactly: an integer and a real of equal magnitude are NOT equal, because the int-vs-real distinction is a first-class part of ClassAd semantics and a frequent source of divergence.
Types ¶
type FloatTolerance ¶
FloatTolerance controls how reals (and the seconds component of times) are compared. Two engines performing the same IEEE-754 operations usually agree bit-for-bit, but transcendental functions (sqrt, pow, ...) may differ in the last ULP across libm versions. A small relative tolerance suppresses that noise without masking genuine semantic differences.
type Kind ¶
type Kind int
Kind enumerates the canonical value types. The set is the union of what both engines can produce; an engine that cannot represent a kind (e.g. the Go engine historically had no absolute-time value) simply never emits it, which surfaces as a divergence.
type Value ¶
type Value struct {
Kind Kind
B bool
I int64
R float64
Off int64 // tz offset (seconds) for KAbstime
S string
List []Value
Map []Attr // sorted by Key for KClassad
}
Value is the parsed form of a canonical encoding.
func FromGoClassAd ¶
FromGoClassAd evaluates every top-level attribute of ad (in the ad's own scope) and returns the canonical classad value. This is the entry point used by the differential fuzzer for the Go engine; it mirrors what the C++ shim does in encodeClassAd.
func FromGoValue ¶
FromGoValue converts a value produced by the native Go evaluator into the canonical representation.
Composite values follow a rule applied identically to the C++ side so that any difference is genuine:
- List elements are encoded in order (the Go evaluator returns them already evaluated).
- A nested ClassAd value is encoded as its attributes sorted by name, each evaluated within the nested ad's own scope. This is a well-defined, symmetric rule; it does not attempt to resolve references into the parent scope (neither engine does so for a detached sub-ad).
maxDepth guards against pathological self-referential structures.