Go - calculate the cidr below one bit of the cidr provided - go

Ask
How do you calculate the cidr below one bit of the cidr provided?
Given:
a network CIDR : (192.168.0.0/16)
wanted result
192.168.0.0/17, 192.168.128.0/17
Using packages such as the default net package and github.com/brotherpowers/ipsubnet, github.com/seancfoley/ipaddress-go/ipaddr did not get the desired results.

To split a network in two, increment the length of the prefix by one. That gives you the lower half. To compute the second half, increment the network part by one (error handling omitted for brevity):
package main
import (
"fmt"
"math/big"
"net/netip"
)
func main() {
p := netip.MustParsePrefix("192.168.0.0/16")
lo, hi := split(p)
fmt.Println(lo, hi) // 192.168.0.0/17 192.168.128.0/17
}
func split(p netip.Prefix) (lo, hi netip.Prefix) {
p = p.Masked()
lo, _ = p.Addr().Prefix(p.Bits() + 1)
delta := big.NewInt(1)
delta.Lsh(delta, uint(lo.Addr().BitLen()-lo.Bits()))
n := new(big.Int).SetBytes(lo.Addr().AsSlice())
n.Add(n, delta)
hiIP, _ := netip.AddrFromSlice(n.Bytes())
hi = netip.PrefixFrom(hiIP, lo.Bits())
return lo, hi
}
https://go.dev/play/p/0HLqUK0RmVC

"Using packages such as the default net package and github.com/brotherpowers/ipsubnet, github.com/seancfoley/ipaddress-go/ipaddr did not get the desired results."
Here is how to do it with github.com/seancfoley/ipaddress-go/ipaddr (note this code also works with IPv6 and any change in prefix length):
package main
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
)
func main() {
cidr := ipaddr.NewIPAddressString("192.168.0.0/16").GetAddress()
for i := cidr.AdjustPrefixLen(1).PrefixBlockIterator(); i.HasNext(); {
fmt.Println(i.Next())
}
}
Output:
192.168.0.0/17
192.168.128.0/17

Related

Golang Regular expression to separate a formula

Need to split something like 1-(a+b-b-d)*100 into 1, a+b-b-d, 100
I tried (\+|-|\*|\/) which will split the string into 1 (a b b d) 100
regular expression pattern:
^(\d+?)\-(\(.+?\))\*(\d+?)$
Go Playground
package main
import (
"log"
"regexp"
)
func main() {
reg := regexp.MustCompile(`^(\d+?)\-(\(.+?\))\*(\d+?)$`)
str := `1-(a+b-b-d)*100`
// see: https://pkg.go.dev/regexp#Regexp.FindAllSubmatch
ret := reg.FindAllSubmatch([]byte(str), -1)
log.Printf("%s %s %s", ret[0][1], ret[0][2], ret[0][3])
}

how to realize mismatch of regexp in golang?

This is a multiple choice question example. I want to get the chinese text like "英国、法国", "加拿大、墨西哥", "葡萄牙、加拿大", "墨西哥、德国" in the content of following code in golang, but it does not work.
package main
import (
"fmt"
"regexp"
"testing"
)
func TestRegex(t *testing.T) {
text := `( B )38.目前,亚马逊美国站后台,除了有美国站点外,还有( )站点。
A.英国、法国B.加拿大、墨西哥
C.葡萄牙、加拿大D.墨西哥、德国
`
fmt.Printf("%q\n", regexp.MustCompile(`[A-E]\.(\S+)?`).FindAllStringSubmatch(text, -1))
fmt.Printf("%q\n", regexp.MustCompile(`[A-E]\.`).Split(text, -1))
}
text:
( B )38.目前,亚马逊美国站后台,除了有美国站点外,还有( )站点。
A.英国、法国B.加拿大、墨西哥
C.葡萄牙、加拿大D.墨西哥、德国
pattern: [A-E]\.(\S+)?
Actual result: [["A.英国、法国B.加拿大、墨西哥" "英国、法国B.加拿大、墨西哥"] ["C.葡萄牙、加拿大D.墨西哥、德国" "葡萄牙、加拿大D.墨西哥、德国"]].
Expect result: [["A.英国、法国" "英国、法国"] ["B.加拿大、墨西哥" "加拿大、墨西哥"] ["C.葡萄牙、加拿大" "葡萄牙、加拿大"] ["D.墨西哥、德国" "墨西哥、德国"]]
I think it might be a greedy mode problem. Because in my code, it reads option A and option B as one option directly.
Non-greedy matching won't solve this, you need positive lookahead, which re2 doesn't support.
As a workaround can just search on the labels and extract the text in between manually.
re := regexp.MustCompile(`[A-E]\.`)
res := re.FindAllStringIndex(text, -1)
results := make([][]string, len(res))
for i, m := range res {
if i < len(res)-1 {
results[i] = []string{text[m[0]:m[1]], text[m[1]:res[i+1][0]]}
} else {
results[i] = []string{text[m[0]:m[1]], text[m[1]:]}
}
}
fmt.Printf("%q\n", results)
Should print
[["A." "英国、法国"] ["B." "加拿大、墨西哥\n"] ["C." "葡萄牙、加拿大"] ["D." "墨西哥、德国\n"]]

Is golang's native string hash function a perfect one?

I've found that function in the golang's source code and want to know whether it's truly a perfect hash function or not.
Is it the correct way to test that?
package main
import (
"fmt"
"strconv"
"unsafe"
)
//go:linkname strhash runtime.strhash
func strhash(p unsafe.Pointer, h uintptr) uintptr
const seed = 666
func main() {
m := make(map[uintptr]string)
for i := 0; i < 1000000000; i++ {
key := strconv.Itoa(i)
hash := strhash(unsafe.Pointer(&key), seed)
_, exist := m[hash]
if exist {
fmt.Println("collision")
break
}
m[hash] = key
}
fmt.Println("finish")
}
As far as I know/can tell, it is not. It uses the AES instructions to create the hash. You might want to check out something like https://github.com/cespare/mph.

Remaining IPs in a range/CIDR

I would like to find the remaining IPs in a range (CIDR).
For example:
prefixLen, bits := n.Mask.Size()
return 1 << (uint64(bits) - uint64(prefixLen)), nil
This return the # of IPs in the range where n is *net.IPNet.
But say if I have few addresses that are already given out and want to know how many are left 'starting' from some IP addresses, how can I do it?
Here's something you could do
package main
import (
"fmt"
"net"
)
func inet_aton(ip string) uint32 {
var rv uint32
b := net.ParseIP(ip).To4()
rv=uint32(b[0]) << 24
rv=rv+uint32(b[1]) << 16
rv=rv+uint32(b[2]) << 8
rv=rv+uint32(b[3])
return rv
}
func main() {
start := inet_aton("192.168.0.1") //192.168.2.0/20 start
fin := inet_aton("192.168.15.255") //ditto end
here := inet_aton("192.168.15.248") //current allocation
fmt.Printf("you have %d remaining\n",(fin-start) - (here-start))
}
Here is a way to do it that works for both IPv4 and IPv6 using the IPAddress Go library. Disclaimer: I am the project manager.
func remaining(cidr string) (used, remaining, total *big.Int) {
cidrStr := ipaddr.NewIPAddressString(cidr)
cidrBlock := cidrStr.GetAddress().ToPrefixBlock()
host := cidrStr.GetHostAddress()
used = new(big.Int).Sub(host.GetValue(), cidrBlock.GetValue())
remaining = new(big.Int).Sub(cidrBlock.GetUpperValue(), host.GetValue())
total = cidrBlock.GetCount()
fmt.Printf("\nHost %s is address number %d of %d addresses in block %s.\n"+
"There remains %d addresses.\n", host, used, total, cidrBlock,
remaining)
return
}
If you want to stick with IPv4, then you can replace big.Int with unsigned int types.
func remainingIPv4(cidr string) (used, remaining uint32, total uint64) {
cidrStr := ipaddr.NewIPAddressString(cidr)
cidrBlock := cidrStr.GetAddress().ToPrefixBlock().ToIPv4()
host := cidrStr.GetHostAddress().ToIPv4()
used = host.Uint32Value() - cidrBlock.Uint32Value()
remaining = cidrBlock.UpperUint32Value() - host.Uint32Value()
total = cidrBlock.GetIPv4Count()
fmt.Printf("\nHost %s is address number %d of %d addresses in block %s.\n"+
"There remains %d addresses.\n", host, used, total, cidrBlock,
remaining)
return
}
Example:
package main
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
"math/big"
)
func main() {
remaining("192.168.2.0/20")
remaining("2001:0db8:85a3::8a2e:0370:7334/64")
remainingIPv4("192.168.2.0/20")
}
Example output:
Host 192.168.2.0 is address number 512 of 4096 addresses in block 192.168.0.0/20.
There remains 3583 addresses.
Host 2001:db8:85a3::8a2e:370:7334 is address number 151930230829876 of 18446744073709551616 addresses in block 2001:db8:85a3::/64.
There remains 18446592143478721739 addresses.
Host 192.168.2.0 is address number 512 of 4096 addresses in block 192.168.0.0/20.
There remains 3583 addresses.

string to big Int in Go?

Is there a way to convert a string (which is essentially a huge number) from string to Big int in Go?
I tried to first convert it into bytes array
array := []byte(string)
Then converting the array into BigInt.
I thought that worked, however, the output was different than the original input. So I'm guessing the conversion didn't do the right thing for some reason.
The numbers I'm dealing with are more than 300 digits long, so I don't think I can use regular int.
Any suggestions of what is the best approach for this?
Package big
import "math/big"
func (*Int) SetString
func (z *Int) SetString(s string, base int) (*Int, bool)
SetString sets z to the value of s, interpreted in the given base, and
returns z and a boolean indicating success. The entire string (not
just a prefix) must be valid for success. If SetString fails, the
value of z is undefined but the returned value is nil.
The base argument must be 0 or a value between 2 and MaxBase. If the
base is 0, the string prefix determines the actual conversion base. A
prefix of “0x” or “0X” selects base 16; the “0” prefix selects base 8,
and a “0b” or “0B” prefix selects base 2. Otherwise the selected base
is 10.
For example,
package main
import (
"fmt"
"math/big"
)
func main() {
n := new(big.Int)
n, ok := n.SetString("314159265358979323846264338327950288419716939937510582097494459", 10)
if !ok {
fmt.Println("SetString: error")
return
}
fmt.Println(n)
}
Playground: https://play.golang.org/p/ZaSOQoqZB_
Output:
314159265358979323846264338327950288419716939937510582097494459
See Example for string to big int conversion.
package main
import (
"fmt"
"log"
"math/big"
)
func main() {
i := new(big.Int)
_, err := fmt.Sscan("18446744073709551617", i)
if err != nil {
log.Println("error scanning value:", err)
} else {
fmt.Println(i)
}
}
Output:
18446744073709551617

Resources