1
1
Fork 1
mirror of https://github.com/oddlama/nixos-extra-modules.git synced 2025-10-10 22:00:39 +02:00

feat: implement pure nix shift

This commit is contained in:
Patrick 2025-04-12 14:30:38 +02:00
parent 8daa7e8318
commit ddc2349760
No known key found for this signature in database
GPG key ID: 451F95EFB8BECD0F
3 changed files with 344 additions and 254 deletions

View file

@ -1,43 +1,40 @@
_inputs: _final: prev: let _inputs: final: prev:
inherit let
(prev.lib) inherit (prev.lib)
filter filter
foldl' foldl'
genAttrs genAttrs
genList
mergeAttrs mergeAttrs
mkMerge mkMerge
stringToCharacters stringToCharacters
substring substring
unique unique
; ;
inherit (final.lib)
bit
;
# Counts how often each element occurrs in xs. # Counts how often each element occurrs in xs.
# Elements must be strings. # Elements must be strings.
countOccurrences = countOccurrences = foldl' (acc: x: acc // { ${x} = (acc.${x} or 0) + 1; }) { };
foldl'
(acc: x: acc // {${x} = (acc.${x} or 0) + 1;})
{};
# Returns all elements in xs that occur at least twice # Returns all elements in xs that occur at least twice
duplicates = xs: let duplicates =
occurrences = countOccurrences xs; xs:
in let
occurrences = countOccurrences xs;
in
unique (filter (x: occurrences.${x} > 1) xs); unique (filter (x: occurrences.${x} > 1) xs);
# Concatenates all given attrsets as if calling a // b in order. # Concatenates all given attrsets as if calling a // b in order.
concatAttrs = foldl' mergeAttrs {}; concatAttrs = foldl' mergeAttrs { };
# True if the path or string starts with / # True if the path or string starts with /
isAbsolutePath = x: substring 0 1 x == "/"; isAbsolutePath = x: substring 0 1 x == "/";
# Merges all given attributes from the given attrsets using mkMerge. # Merges all given attributes from the given attrsets using mkMerge.
# Useful to merge several top-level configs in a module. # Useful to merge several top-level configs in a module.
mergeToplevelConfigs = keys: attrs: mergeToplevelConfigs = keys: attrs: genAttrs keys (attr: mkMerge (map (x: x.${attr} or { }) attrs));
genAttrs keys (attr: mkMerge (map (x: x.${attr} or {}) attrs));
# Calculates base^exp, but careful, this overflows for results > 2^62
pow = base: exp: foldl' (a: x: x * a) 1 (genList (_: base) exp);
hexLiteralValues = { hexLiteralValues = {
"0" = 0; "0" = 0;
@ -66,19 +63,17 @@ _inputs: _final: prev: let
# Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63), # Converts the given hex string to an integer. Only reliable for inputs in [0, 2^63),
# after that the sign bit will overflow. # after that the sign bit will overflow.
hexToDec = v: foldl' (acc: x: acc * 16 + hexLiteralValues.${x}) 0 (stringToCharacters v); hexToDec = v: foldl' (acc: x: (bit.left acc 4) + hexLiteralValues.${x}) 0 (stringToCharacters v);
in { in
lib = {
prev.lib lib = prev.lib // {
// { inherit
inherit hexToDec
concatAttrs concatAttrs
countOccurrences countOccurrences
duplicates duplicates
hexToDec isAbsolutePath
isAbsolutePath mergeToplevelConfigs
mergeToplevelConfigs ;
pow };
;
};
} }

View file

@ -1,6 +1,6 @@
inputs: _final: prev: let inputs: _final: prev:
inherit let
(prev.lib) inherit (prev.lib)
all all
any any
assertMsg assertMsg
@ -22,24 +22,29 @@ inputs: _final: prev: let
; ;
# From misc.nix # From misc.nix
inherit inherit (prev.lib)
(prev.lib)
hexToDec hexToDec
pow
; ;
# IP address math library # IP address math library
# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba # https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba
# Plus some extensions by us # Plus some extensions by us
libNet = libNet =
(import "${inputs.lib-net}/net.nix" { (import ./netu.nix {
inherit (inputs.nixpkgs) lib; inherit (inputs.nixpkgs) lib;
}) }).lib.net;
.lib in
.net; {
in {
lib = recursiveUpdate prev.lib { lib = recursiveUpdate prev.lib {
net = recursiveUpdate (removeAttrs libNet ["types"]) { inherit (libNet)
arithmetic
typechecks
bit
implementations
parsers
;
net = recursiveUpdate (removeAttrs libNet [ "types" ]) {
cidr = rec { cidr = rec {
# host :: (ip | mac | integer) -> cidr -> ip # host :: (ip | mac | integer) -> cidr -> ip
# #
@ -58,11 +63,13 @@ in {
# "192.168.1.0" # "192.168.1.0"
# > net.cidr.host (-257) "192.168.1.0/24" # > net.cidr.host (-257) "192.168.1.0/24"
# <fails with an error message> # <fails with an error message>
host = i: n: let host =
cap = libNet.cidr.capacity n; i: n:
in let
cap = libNet.cidr.capacity n;
in
assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}"; assert assertMsg (i >= (-cap) && i < cap) "The host ${toString i} lies outside of ${n}";
libNet.cidr.host i n; libNet.cidr.host i n;
# hostCidr :: (ip | mac | integer) -> cidr -> cidr # hostCidr :: (ip | mac | integer) -> cidr -> cidr
# #
# Returns the nth host in the given cidr range (like cidr.host) # Returns the nth host in the given cidr range (like cidr.host)
@ -103,37 +110,30 @@ in {
# #
# > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"] # > net.cidr.mergev4 ["192.168.1.1/24" "192.168.6.1/32"]
# "192.168.0.0/21" # "192.168.0.0/21"
mergev4 = addrs_: let mergev4 =
# Append /32 if necessary addrs_:
addrs = map (x: let
if hasInfix "/" x # Append /32 if necessary
then x addrs = map (x: if hasInfix "/" x then x else "${x}/32") addrs_;
else "${x}/32") # The smallest occurring length is the first we need to start checking, since
addrs_; # any greater cidr length represents a smaller address range which
# The smallest occurring length is the first we need to start checking, since # wouldn't contain all of the original addresses.
# any greater cidr length represents a smaller address range which startLength = foldl' min 32 (map libNet.cidr.length addrs);
# wouldn't contain all of the original addresses. possibleLengths = reverseList (range 0 startLength);
startLength = foldl' min 32 (map libNet.cidr.length addrs); # The first ip address will be "expanded" in cidr length until it covers all other
possibleLengths = reverseList (range 0 startLength); # used addresses.
# The first ip address will be "expanded" in cidr length until it covers all other firstIp = ip (head addrs);
# used addresses. # Return the first (i.e. greatest length -> smallest prefix) cidr length
firstIp = ip (head addrs); # in the list that covers all used addresses
# Return the first (i.e. greatest length -> smallest prefix) cidr length bestLength = head (
# in the list that covers all used addresses filter
bestLength = head (filter # All given addresses must be contained by the generated address.
# All given addresses must be contained by the generated address. (len: all (x: libNet.cidr.contains (ip x) (libNet.cidr.make len firstIp)) addrs)
(len: possibleLengths
all (x: );
libNet.cidr.contains in
(ip x)
(libNet.cidr.make len firstIp))
addrs)
possibleLengths);
in
assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses"; assert assertMsg (!any (hasInfix ":") addrs) "mergev4 cannot operate on ipv6 addresses";
if addrs == [] if addrs == [ ] then null else libNet.cidr.make bestLength firstIp;
then null
else libNet.cidr.make bestLength firstIp;
# mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null) # mergev6 :: [cidrv6 | ipv6] -> (cidrv6 | null)
# #
# Returns the smallest cidr network that includes all given networks. # Returns the smallest cidr network that includes all given networks.
@ -143,48 +143,44 @@ in {
# #
# > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"] # > net.cidr.mergev6 ["fd00:dead:cafe::/64" "fd00:fd12:3456:7890::/56"]
# "fd00:c000::/18" # "fd00:c000::/18"
mergev6 = addrs_: let mergev6 =
# Append /128 if necessary addrs_:
addrs = map (x: let
if hasInfix "/" x # Append /128 if necessary
then x addrs = map (x: if hasInfix "/" x then x else "${x}/128") addrs_;
else "${x}/128") # The smallest occurring length is the first we need to start checking, since
addrs_; # any greater cidr length represents a smaller address range which
# The smallest occurring length is the first we need to start checking, since # wouldn't contain all of the original addresses.
# any greater cidr length represents a smaller address range which startLength = foldl' min 128 (map libNet.cidr.length addrs);
# wouldn't contain all of the original addresses. possibleLengths = reverseList (range 0 startLength);
startLength = foldl' min 128 (map libNet.cidr.length addrs); # The first ip address will be "expanded" in cidr length until it covers all other
possibleLengths = reverseList (range 0 startLength); # used addresses.
# The first ip address will be "expanded" in cidr length until it covers all other firstIp = ip (head addrs);
# used addresses. # Return the first (i.e. greatest length -> smallest prefix) cidr length
firstIp = ip (head addrs); # in the list that covers all used addresses
# Return the first (i.e. greatest length -> smallest prefix) cidr length bestLength = head (
# in the list that covers all used addresses filter
bestLength = head (filter # All given addresses must be contained by the generated address.
# All given addresses must be contained by the generated address. (len: all (x: libNet.cidr.contains (ip x) (libNet.cidr.make len firstIp)) addrs)
(len: possibleLengths
all (x: );
libNet.cidr.contains in
(ip x)
(libNet.cidr.make len firstIp))
addrs)
possibleLengths);
in
assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses"; assert assertMsg (all (hasInfix ":") addrs) "mergev6 cannot operate on ipv4 addresses";
if addrs == [] if addrs == [ ] then null else libNet.cidr.make bestLength firstIp;
then null
else libNet.cidr.make bestLength firstIp;
# merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); } # merge :: [cidr] -> { cidrv4 = (cidrv4 | null); cidrv6 = (cidrv4 | null); }
# #
# Returns the smallest cidr network that includes all given networks, # Returns the smallest cidr network that includes all given networks,
# but yields two separate result for all given ipv4 and ipv6 addresses. # but yields two separate result for all given ipv4 and ipv6 addresses.
# Equivalent to calling mergev4 and mergev6 on a partition individually. # Equivalent to calling mergev4 and mergev6 on a partition individually.
merge = addrs: let merge =
v4_and_v6 = partition (hasInfix ":") addrs; addrs:
in { let
cidrv4 = mergev4 v4_and_v6.wrong; v4_and_v6 = partition (hasInfix ":") addrs;
cidrv6 = mergev6 v4_and_v6.right; in
}; {
cidrv4 = mergev4 v4_and_v6.wrong;
cidrv6 = mergev6 v4_and_v6.right;
};
# assignIps :: cidr -> [int | ip] -> [string] -> [ip] # assignIps :: cidr -> [int | ip] -> [string] -> [ip]
# #
# Assigns a semi-stable ip address from the given cidr network to each hostname. # Assigns a semi-stable ip address from the given cidr network to each hostname.
@ -203,68 +199,76 @@ in {
# #
# > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"] # > net.cidr.assignIps "192.168.100.1/24" [202 "192.168.100.74"] ["a" "b" "c"]
# { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; } # { a = "192.168.100.203"; b = "192.168.100.75"; c = "192.168.100.226"; }
assignIps = net: reserved: hosts: let assignIps =
cidrSize = libNet.cidr.size net; net: reserved: hosts:
capacity = libNet.cidr.capacity net; let
# The base address of the network. Used to convert ip-based reservations to offsets cidrSize = libNet.cidr.size net;
baseAddr = host 0 net; capacity = libNet.cidr.capacity net;
# Reserve some values for the network, host and broadcast address. # The base address of the network. Used to convert ip-based reservations to offsets
# The network and broadcast address should never be used, and we baseAddr = host 0 net;
# want to reserve the host address for the host. We also convert # Reserve some values for the network, host and broadcast address.
# any ips to offsets here. # The network and broadcast address should never be used, and we
init = unique ( # want to reserve the host address for the host. We also convert
[0 (capacity - 1)] # any ips to offsets here.
++ flip map reserved (x: init = unique (
if builtins.typeOf x == "int" [
then x 0
else -(libNet.ip.diff baseAddr x)) (capacity - 1)
); ]
nHosts = builtins.length hosts; ++ flip map reserved (x: if builtins.typeOf x == "int" then x else -(libNet.ip.diff baseAddr x))
nInit = builtins.length init; );
# Pre-sort all hosts, to ensure ordering invariance nHosts = builtins.length hosts;
sortedHosts = nInit = builtins.length init;
warnIf # Pre-sort all hosts, to ensure ordering invariance
((nInit + nHosts) > 0.3 * capacity) sortedHosts =
"assignIps: hash stability may be degraded since utilization is >30%" warnIf ((nInit + nHosts) > 0.3 * capacity)
(builtins.sort builtins.lessThan hosts); "assignIps: hash stability may be degraded since utilization is >30%"
# Generates a hash (i.e. offset value) for a given hostname (builtins.sort builtins.lessThan hosts);
hashElem = x: # Generates a hash (i.e. offset value) for a given hostname
builtins.bitAnd (capacity - 1) hashElem =
(hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x))); x:
# Do linear probing. Returns the first unused value at or after the given value. builtins.bitAnd (capacity - 1) (
probe = avoid: value: hexToDec (builtins.substring 0 16 (builtins.hashString "sha256" x))
if elem value avoid );
# TODO lib.mod # Do linear probing. Returns the first unused value at or after the given value.
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue probe =
# class of x modulo 2^n, so we can use bitAnd instead. avoid: value:
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1)) if
else value; elem value avoid
# Hash a new element and avoid assigning any existing values. # TODO lib.mod
assignOne = { # Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
assigned, # class of x modulo 2^n, so we can use bitAnd instead.
used, then
}: x: let probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
value = probe used (hashElem x); else
in { value;
assigned = # Hash a new element and avoid assigning any existing values.
assigned assignOne =
// { {
${x} = host value net; assigned,
used,
}:
x:
let
value = probe used (hashElem x);
in
{
assigned = assigned // {
${x} = host value net;
};
used = [ value ] ++ used;
}; };
used = [value] ++ used; in
}; assert assertMsg (
in cidrSize >= 2 && cidrSize <= 62
assert assertMsg (cidrSize >= 2 && cidrSize <= 62) ) "assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
"assignIps: cidrSize=${toString cidrSize} is not in [2, 62].";
assert assertMsg (nHosts <= capacity - nInit) assert assertMsg (nHosts <= capacity - nInit)
"assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})"; "assignIps: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order # Assign an ip in the subnet to each element, in order
(foldl' assignOne { (foldl' assignOne {
assigned = {}; assigned = { };
used = init; used = init;
} } sortedHosts).assigned;
sortedHosts)
.assigned;
}; };
ip = rec { ip = rec {
# Checks whether the given address (with or without cidr notation) is an ipv4 address. # Checks whether the given address (with or without cidr notation) is an ipv4 address.
@ -275,11 +279,14 @@ in {
mac = { mac = {
# Adds offset to the given base address and ensures the result is in # Adds offset to the given base address and ensures the result is in
# a locally administered range by replacing the second nibble with a 2. # a locally administered range by replacing the second nibble with a 2.
addPrivate = base: offset: let addPrivate =
added = libNet.mac.add base offset; base: offset:
pre = substring 0 1 added; let
suf = substring 2 (-1) added; added = libNet.mac.add base offset;
in "${pre}2${suf}"; pre = substring 0 1 added;
suf = substring 2 (-1) added;
in
"${pre}2${suf}";
# assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac] # assignMacs :: mac (base) -> int (size) -> [int | mac] (reserved) -> [string] (hosts) -> [mac]
# #
# Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname. # Assigns a semi-stable MAC address starting in [base, base + 2^size) to each hostname.
@ -297,63 +304,64 @@ in {
# #
# > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"] # > net.mac.assignMacs "11:22:33:00:00:00" 24 ["11:22:33:1b:bd:ca"] ["a" "b" "c"]
# { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; } # { a = "11:22:33:1b:bd:cb"; b = "11:22:33:39:59:4a"; c = "11:22:33:50:7a:e2"; }
assignMacs = base: size: reserved: hosts: let assignMacs =
capacity = pow 2 size; base: size: reserved: hosts:
baseAsInt = libNet.mac.diff base "00:00:00:00:00:00"; let
init = unique ( capacity = libNet.bit.left 1 size;
flip map reserved (x: baseAsInt = libNet.net.mac.diff base "00:00:00:00:00:00";
if builtins.typeOf x == "int" init = unique (
then x flip map reserved (x: if builtins.typeOf x == "int" then x else libNet.net.mac.diff x base)
else libNet.mac.diff x base) );
); nHosts = builtins.length hosts;
nHosts = builtins.length hosts; nInit = builtins.length init;
nInit = builtins.length init; # Pre-sort all hosts, to ensure ordering invariance
# Pre-sort all hosts, to ensure ordering invariance sortedHosts =
sortedHosts = warnIf ((nInit + nHosts) > 0.3 * capacity)
warnIf "assignMacs: hash stability may be degraded since utilization is >30%"
((nInit + nHosts) > 0.3 * capacity) (builtins.sort builtins.lessThan hosts);
"assignMacs: hash stability may be degraded since utilization is >30%" # Generates a hash (i.e. offset value) for a given hostname
(builtins.sort builtins.lessThan hosts); hashElem =
# Generates a hash (i.e. offset value) for a given hostname x: builtins.bitAnd (capacity - 1) (hexToDec (substring 0 16 (builtins.hashString "sha256" x)));
hashElem = x: # Do linear probing. Returns the first unused value at or after the given value.
builtins.bitAnd (capacity - 1) probe =
(hexToDec (substring 0 16 (builtins.hashString "sha256" x))); avoid: value:
# Do linear probing. Returns the first unused value at or after the given value. if
probe = avoid: value: elem value avoid
if elem value avoid # TODO lib.mod
# TODO lib.mod # Poor man's modulo, because nix has no modulo. Luckily we operate on a residue
# Poor man's modulo, because nix has no modulo. Luckily we operate on a residue # class of x modulo 2^n, so we can use bitAnd instead.
# class of x modulo 2^n, so we can use bitAnd instead. then
then probe avoid (builtins.bitAnd (capacity - 1) (value + 1)) probe avoid (builtins.bitAnd (capacity - 1) (value + 1))
else value; else
# Hash a new element and avoid assigning any existing values. value;
assignOne = { # Hash a new element and avoid assigning any existing values.
assigned, assignOne =
used, {
}: x: let assigned,
value = probe used (hashElem x); used,
in { }:
assigned = x:
assigned let
// { value = probe used (hashElem x);
${x} = libNet.mac.add value base; in
{
assigned = assigned // {
${x} = libNet.net.mac.add value base;
};
used = [ value ] ++ used;
}; };
used = [value] ++ used; in
}; assert assertMsg (size >= 2 && size <= 62) "assignMacs: size=${toString size} is not in [2, 62].";
in assert assertMsg (
assert assertMsg (size >= 2 && size <= 62) builtins.bitAnd (capacity - 1) baseAsInt == 0
"assignMacs: size=${toString size} is not in [2, 62]."; ) "assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
assert assertMsg (builtins.bitAnd (capacity - 1) baseAsInt == 0)
"assignMacs: the size=${toString size} least significant bits of the base mac address must be 0.";
assert assertMsg (nHosts <= capacity - nInit) assert assertMsg (nHosts <= capacity - nInit)
"assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})"; "assignMacs: number of hosts (${toString nHosts}) must be <= capacity (${toString capacity}) - reserved (${toString nInit})";
# Assign an ip in the subnet to each element, in order # Assign an ip in the subnet to each element, in order
(foldl' assignOne { (foldl' assignOne {
assigned = {}; assigned = { };
used = init; used = init;
} } sortedHosts).assigned;
sortedHosts)
.assigned;
}; };
}; };
types.net = libNet.types; types.net = libNet.types;

View file

@ -460,20 +460,111 @@ let
bit = bit =
let let
shift = lut = {
n: x: "0" = 1;
if n < 0 then "1" = 2;
x * math.pow 2 (-n) "2" = 4;
"3" = 8;
"4" = 16;
"5" = 32;
"6" = 64;
"7" = 128;
"8" = 256;
"9" = 512;
"10" = 1024;
"11" = 2048;
"12" = 4096;
"13" = 8192;
"14" = 16384;
"15" = 32768;
"16" = 65536;
"17" = 131072;
"18" = 262144;
"19" = 524288;
"20" = 1048576;
"21" = 2097152;
"22" = 4194304;
"23" = 8388608;
"24" = 16777216;
"25" = 33554432;
"26" = 67108864;
"27" = 134217728;
"28" = 268435456;
"29" = 536870912;
"30" = 1073741824;
"31" = 2147483648;
"32" = 4294967296;
"33" = 8589934592;
"34" = 17179869184;
"35" = 34359738368;
"36" = 68719476736;
"37" = 137438953472;
"38" = 274877906944;
"39" = 549755813888;
"40" = 1099511627776;
"41" = 2199023255552;
"42" = 4398046511104;
"43" = 8796093022208;
"44" = 17592186044416;
"45" = 35184372088832;
"46" = 70368744177664;
"47" = 140737488355328;
"48" = 281474976710656;
"49" = 562949953421312;
"50" = 1125899906842624;
"51" = 2251799813685248;
"52" = 4503599627370496;
"53" = 9007199254740992;
"54" = 18014398509481984;
"55" = 36028797018963968;
"56" = 72057594037927936;
"57" = 144115188075855872;
"58" = 288230376151711744;
"59" = 576460752303423488;
"60" = 1152921504606846976;
"61" = 2305843009213693952;
"62" = 4611686018427387904;
};
intmin = (-9223372036854775807) - 1;
intmax = 9223372036854775807;
left =
a: b:
if a >= 64 then
# It's allowed to shift out all bits
0
else if a == 0 then
b
else if a < 0 then
right (-a) b
else else
let let
safeDiv = n: d: if d == 0 then 0 else n / d; inv = 63 - a;
d = math.pow 2 n; mask = if inv == 63 then intmax else lut.${toString inv} - 1;
masked = and b mask;
checker = if inv == 63 then intmin else lut.${toString inv};
negate = (and b checker) != 0;
mult = if a == 63 then intmin else lut.${toString a};
result = masked * mult;
in in
if x < 0 then not (safeDiv (not x) d) else safeDiv x d; if !negate then result else intmin + result;
left = n: shift (-n); right =
a: b:
right = shift; if a >= 64 then
0
else if a == 0 then
b
else if a < 0 then
left (-a) b
else
let
masked = and b intmax;
negate = b < 0;
result = masked / lut.${toString a};
inv = 63 - a;
highest_bit = lut.${toString inv};
in
if !negate then result else result + highest_bit;
and = builtins.bitAnd; and = builtins.bitAnd;
@ -505,15 +596,6 @@ let
clamp = clamp =
a: b: c: a: b: c:
max a (min b c); max a (min b c);
pow =
x: n:
if n == 0 then
1
else if bit.and n 1 != 0 then
x * pow (x * x) ((n - 1) / 2)
else
pow (x * x) (n / 2);
}; };
parsers = parsers =
@ -951,8 +1033,6 @@ let
lower = bit.mask 4 n; lower = bit.mask 4 n;
in in
"${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}"; "${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}";
in
let
a = bit.mask 8 (bit.right 40 address.mac); a = bit.mask 8 (bit.right 40 address.mac);
b = bit.mask 8 (bit.right 32 address.mac); b = bit.mask 8 (bit.right 32 address.mac);
c = bit.mask 8 (bit.right 24 address.mac); c = bit.mask 8 (bit.right 24 address.mac);
@ -1390,6 +1470,13 @@ in
{ {
lib = { lib = {
inherit net; inherit
arithmetic
net
typechecks
parsers
implementations
bit
;
}; };
} }