utility

package
v1.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 1, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

Utility

General-purpose utility functions for Go applications.

Overview

The utility package provides helper functions for common tasks including runtime introspection, type information extraction, and network utilities. These utilities simplify everyday programming tasks that would otherwise require boilerplate code.

Features

  • Caller Information - Retrieve file, line, function, and goroutine details
  • Type Introspection - Get type names from any value
  • CIDR Utilities - Check IP containment and enumerate IP ranges
  • Runtime Reflection - Access stack and goroutine information

Installation

go get -u github.com/common-library/go/utility

Quick Start

import "github.com/common-library/go/utility"

// Get caller information
callerInfo, _ := utility.GetCallerInfo(1)
fmt.Printf("Called from %s:%d\n", callerInfo.FileName, callerInfo.Line)

// Get type name
typeName := utility.GetTypeName(42)
fmt.Println(typeName) // Output: "int"

// Check CIDR containment
contains, _ := utility.WhetherCidrContainsIp("192.168.1.0/24", "192.168.1.100")
fmt.Println(contains) // Output: true

API Reference

CallerInfo Type
type CallerInfo struct {
    PackageName  string
    FileName     string
    FunctionName string
    Line         int
    GoroutineID  int
}

Contains details about a function call location.

GetCallerInfo
func GetCallerInfo(numberOfStackFramesToAscend int) (CallerInfo, error)

Retrieves caller information from the stack.

Parameters:

  • numberOfStackFramesToAscend - Stack frames to skip (0=self, 1=caller, 2=caller's caller)

Returns:

  • CallerInfo - Caller details
  • error - Error if stack retrieval fails
GetTypeName
func GetTypeName(value any) string

Returns the type name of any value.

Parameters:

  • value - Any value to inspect

Returns:

  • string - Type name (e.g., "int", "*User", "[]string")
WhetherCidrContainsIp
func WhetherCidrContainsIp(cidr, ip string) (bool, error)

Checks if an IP is within a CIDR range.

Parameters:

  • cidr - CIDR notation (e.g., "192.168.1.0/24")
  • ip - IP address to check

Returns:

  • bool - true if IP is in range
  • error - Error if CIDR parsing fails
GetAllIpsOfCidr
func GetAllIpsOfCidr(cidr string) ([]string, error)

Returns all usable IPs in a CIDR range (excludes network and broadcast).

Parameters:

  • cidr - CIDR notation

Returns:

  • []string - List of IP addresses
  • error - Error if CIDR parsing fails

Complete Examples

Caller Information for Logging
package main

import (
    "fmt"
    "log"
    "github.com/common-library/go/utility"
)

func logWithCaller(message string) {
    info, err := utility.GetCallerInfo(1)
    if err != nil {
        log.Println(message)
        return
    }
    
    log.Printf("[%s:%d %s] %s",
        info.FileName,
        info.Line,
        info.FunctionName,
        message,
    )
}

func processData() {
    logWithCaller("Starting data processing")
    // ... process data ...
    logWithCaller("Data processing complete")
}

func main() {
    processData()
    // Output: [main.go:21 main.processData] Starting data processing
}
Stack Trace Debugging
package main

import (
    "fmt"
    "github.com/common-library/go/utility"
)

func printStack(depth int) {
    fmt.Println("Stack trace:")
    for i := 0; i < depth; i++ {
        info, err := utility.GetCallerInfo(i)
        if err != nil {
            break
        }
        
        fmt.Printf("  #%d %s:%d %s (goroutine %d)\n",
            i,
            info.FileName,
            info.Line,
            info.FunctionName,
            info.GoroutineID,
        )
    }
}

func level3() {
    printStack(5)
}

func level2() {
    level3()
}

func level1() {
    level2()
}

func main() {
    level1()
}
Type-Based Processing
package main

import (
    "fmt"
    "github.com/common-library/go/utility"
)

func processValue(value any) {
    typeName := utility.GetTypeName(value)
    
    switch typeName {
    case "int":
        fmt.Printf("Processing integer: %d\n", value)
    case "string":
        fmt.Printf("Processing string: %s\n", value)
    case "[]int":
        fmt.Printf("Processing int slice: %v\n", value)
    default:
        fmt.Printf("Processing %s: %v\n", typeName, value)
    }
}

func main() {
    processValue(42)
    processValue("hello")
    processValue([]int{1, 2, 3})
    processValue(map[string]int{"a": 1})
}
CIDR IP Validation
package main

import (
    "fmt"
    "log"
    "github.com/common-library/go/utility"
)

func validateIPInNetwork(cidr, ip string) {
    contains, err := utility.WhetherCidrContainsIp(cidr, ip)
    if err != nil {
        log.Fatalf("Invalid CIDR: %v", err)
    }
    
    if contains {
        fmt.Printf("✓ %s is in %s\n", ip, cidr)
    } else {
        fmt.Printf("✗ %s is NOT in %s\n", ip, cidr)
    }
}

func main() {
    cidr := "192.168.1.0/24"
    
    validateIPInNetwork(cidr, "192.168.1.100")  // ✓
    validateIPInNetwork(cidr, "192.168.1.1")    // ✓
    validateIPInNetwork(cidr, "192.168.2.100")  // ✗
    validateIPInNetwork(cidr, "10.0.0.1")       // ✗
}
IP Range Enumeration
package main

import (
    "fmt"
    "log"
    "github.com/common-library/go/utility"
)

func main() {
    // Small subnet
    cidr := "192.168.1.0/29"
    
    ips, err := utility.GetAllIpsOfCidr(cidr)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("CIDR: %s\n", cidr)
    fmt.Printf("Usable IPs: %d\n", len(ips))
    fmt.Println("IP addresses:")
    for _, ip := range ips {
        fmt.Printf("  - %s\n", ip)
    }
    
    // Output:
    // CIDR: 192.168.1.0/29
    // Usable IPs: 6
    // IP addresses:
    //   - 192.168.1.1
    //   - 192.168.1.2
    //   - 192.168.1.3
    //   - 192.168.1.4
    //   - 192.168.1.5
    //   - 192.168.1.6
}
IP Allocation Manager
package main

import (
    "fmt"
    "log"
    "github.com/common-library/go/utility"
)

type IPPool struct {
    cidr      string
    allocated map[string]bool
    available []string
}

func NewIPPool(cidr string) (*IPPool, error) {
    ips, err := utility.GetAllIpsOfCidr(cidr)
    if err != nil {
        return nil, err
    }
    
    return &IPPool{
        cidr:      cidr,
        allocated: make(map[string]bool),
        available: ips,
    }, nil
}

func (p *IPPool) Allocate() (string, error) {
    if len(p.available) == 0 {
        return "", fmt.Errorf("no available IPs")
    }
    
    ip := p.available[0]
    p.available = p.available[1:]
    p.allocated[ip] = true
    
    return ip, nil
}

func (p *IPPool) Release(ip string) {
    if p.allocated[ip] {
        delete(p.allocated, ip)
        p.available = append(p.available, ip)
    }
}

func (p *IPPool) Stats() {
    fmt.Printf("CIDR: %s\n", p.cidr)
    fmt.Printf("Allocated: %d\n", len(p.allocated))
    fmt.Printf("Available: %d\n", len(p.available))
}

func main() {
    pool, err := NewIPPool("10.0.0.0/28")
    if err != nil {
        log.Fatal(err)
    }
    
    // Allocate IPs
    ip1, _ := pool.Allocate()
    ip2, _ := pool.Allocate()
    ip3, _ := pool.Allocate()
    
    fmt.Printf("Allocated: %s, %s, %s\n", ip1, ip2, ip3)
    pool.Stats()
    
    // Release one
    pool.Release(ip2)
    pool.Stats()
}
Generic Type Checker
package main

import (
    "fmt"
    "github.com/common-library/go/utility"
)

func isPointerType(value any) bool {
    typeName := utility.GetTypeName(value)
    return len(typeName) > 0 && typeName[0] == '*'
}

func isSliceType(value any) bool {
    typeName := utility.GetTypeName(value)
    return len(typeName) > 1 && typeName[0:2] == "[]"
}

func isMapType(value any) bool {
    typeName := utility.GetTypeName(value)
    return len(typeName) > 2 && typeName[0:3] == "map"
}

func main() {
    var x int = 42
    var ptr *int = &x
    var slice []int = []int{1, 2, 3}
    var m map[string]int = map[string]int{"a": 1}
    
    fmt.Printf("x is pointer: %v\n", isPointerType(x))     // false
    fmt.Printf("ptr is pointer: %v\n", isPointerType(ptr)) // true
    fmt.Printf("slice is slice: %v\n", isSliceType(slice)) // true
    fmt.Printf("m is map: %v\n", isMapType(m))             // true
}
Goroutine Tracking
package main

import (
    "fmt"
    "sync"
    "time"
    "github.com/common-library/go/utility"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    
    info, _ := utility.GetCallerInfo(0)
    fmt.Printf("Worker %d running in goroutine %d\n", id, info.GoroutineID)
    
    time.Sleep(100 * time.Millisecond)
}

func main() {
    var wg sync.WaitGroup
    
    mainInfo, _ := utility.GetCallerInfo(0)
    fmt.Printf("Main goroutine ID: %d\n", mainInfo.GoroutineID)
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
}

Best Practices

1. Handle Errors from GetCallerInfo
// Good: Check error
info, err := utility.GetCallerInfo(1)
if err != nil {
    log.Printf("Failed to get caller info: %v", err)
    return
}

// Avoid: Ignore error
info, _ := utility.GetCallerInfo(1)
2. Use Appropriate Stack Depth
// Good: Skip correct number of frames
func logHelper(msg string) {
    info, _ := utility.GetCallerInfo(1) // Skip logHelper itself
    log.Printf("[%s:%d] %s", info.FileName, info.Line, msg)
}

// Avoid: Wrong depth
func logHelper(msg string) {
    info, _ := utility.GetCallerInfo(0) // Points to logHelper, not caller
}
3. Be Cautious with Large CIDR Ranges
// Good: Check size before enumerating
cidr := "10.0.0.0/16" // 65,534 IPs
// Consider: Do you really need all IPs in memory?

// Risky: Very large range
cidr := "10.0.0.0/8" // 16,777,214 IPs - may exhaust memory
ips, _ := utility.GetAllIpsOfCidr(cidr) // Dangerous!
4. Cache Type Names for Performance
// Good: Cache type names if checking repeatedly
typeCache := make(map[any]string)

func getCachedTypeName(value any) string {
    if name, ok := typeCache[value]; ok {
        return name
    }
    name := utility.GetTypeName(value)
    typeCache[value] = name
    return name
}

Performance Tips

  1. GetCallerInfo - Has runtime overhead, use sparingly in hot paths
  2. GetTypeName - Uses reflection, cache results when possible
  3. GetAllIpsOfCidr - Memory intensive for large ranges, consider pagination
  4. CIDR Checks - WhetherCidrContainsIp is efficient, prefer over full enumeration

Testing

func TestGetCallerInfo(t *testing.T) {
    info, err := utility.GetCallerInfo(0)
    if err != nil {
        t.Fatalf("GetCallerInfo failed: %v", err)
    }
    
    if info.FunctionName == "" {
        t.Error("Function name should not be empty")
    }
    
    if info.Line <= 0 {
        t.Error("Line number should be positive")
    }
}

func TestGetTypeName(t *testing.T) {
    tests := []struct {
        value    any
        expected string
    }{
        {42, "int"},
        {"hello", "string"},
        {[]int{}, "[]int"},
        {map[string]int{}, "map[string]int"},
    }
    
    for _, tt := range tests {
        result := utility.GetTypeName(tt.value)
        if result != tt.expected {
            t.Errorf("Expected %s, got %s", tt.expected, result)
        }
    }
}

func TestCIDR(t *testing.T) {
    contains, err := utility.WhetherCidrContainsIp("192.168.1.0/24", "192.168.1.100")
    if err != nil {
        t.Fatal(err)
    }
    
    if !contains {
        t.Error("IP should be in range")
    }
}

Dependencies

  • runtime - Go standard library
  • reflect - Go standard library
  • net - Go standard library

Further Reading

Documentation

Overview

Package utility provides general-purpose utility functions.

This package offers various helper functions for common tasks including runtime introspection, type information, and network utilities.

Features:

  • Caller information retrieval (file, line, function, goroutine ID)
  • Type name extraction
  • CIDR network utilities

Example:

callerInfo, _ := utility.GetCallerInfo(1)
fmt.Printf("Called from %s:%d\n", callerInfo.FileName, callerInfo.Line)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetAllIpsOfCidr

func GetAllIpsOfCidr(cidr string) ([]string, error)

GetAllIpsOfCidr returns all usable IP addresses in a CIDR range.

This function generates a list of all IP addresses within the specified CIDR notation, excluding the network and broadcast addresses.

Parameters:

  • cidr: CIDR notation string (e.g., "192.168.1.0/24")

Returns:

  • []string: Slice of IP address strings (excluding network and broadcast IPs)
  • error: Error if CIDR parsing fails

For example, "192.168.1.0/30" contains 4 addresses:

  • 192.168.1.0 (network address, excluded)
  • 192.168.1.1 (returned)
  • 192.168.1.2 (returned)
  • 192.168.1.3 (broadcast address, excluded)

Warning: Large CIDR ranges (e.g., /8) will generate millions of IPs and may consume significant memory. Use with caution.

Example with small range:

ips, err := utility.GetAllIpsOfCidr("192.168.1.0/30")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Found %d usable IPs:\n", len(ips))
for _, ip := range ips {
    fmt.Println(ip)
}
// Output:
// Found 2 usable IPs:
// 192.168.1.1
// 192.168.1.2

Example with /24 subnet:

ips, err := utility.GetAllIpsOfCidr("10.0.1.0/24")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Subnet has %d usable IPs\n", len(ips))
// Output: Subnet has 254 usable IPs

Example for IP allocation:

cidr := "172.16.0.0/28"
allIps, _ := utility.GetAllIpsOfCidr(cidr)

// Allocate first 5 IPs
allocated := allIps[:5]
available := allIps[5:]

fmt.Printf("Allocated: %v\n", allocated)
fmt.Printf("Available: %d IPs\n", len(available))

func GetTypeName

func GetTypeName(value any) string

GetTypeName returns the type name of a value as a string.

This function uses reflection to determine the runtime type of any value and returns its string representation.

Parameters:

  • value: Any value to get the type name from

Returns:

  • string: Type name (e.g., "int", "string", "*MyStruct", "[]int")

The returned string format:

  • Built-in types: "int", "string", "bool", etc.
  • Pointers: "*TypeName"
  • Slices: "[]TypeName"
  • Maps: "map[KeyType]ValueType"
  • Structs: "package.StructName" or "main.StructName"

Example with basic types:

typeName := utility.GetTypeName(42)
fmt.Println(typeName) // Output: "int"

typeName = utility.GetTypeName("hello")
fmt.Println(typeName) // Output: "string"

Example with complex types:

type User struct {
    Name string
    Age  int
}

user := User{Name: "Alice", Age: 30}
typeName := utility.GetTypeName(user)
fmt.Println(typeName) // Output: "main.User"

typeName = utility.GetTypeName(&user)
fmt.Println(typeName) // Output: "*main.User"

Example with collections:

slice := []int{1, 2, 3}
fmt.Println(utility.GetTypeName(slice)) // Output: "[]int"

myMap := map[string]int{"a": 1}
fmt.Println(utility.GetTypeName(myMap)) // Output: "map[string]int"

Example for type checking:

func processValue(value any) {
    typeName := utility.GetTypeName(value)
    switch typeName {
    case "int":
        fmt.Println("Processing integer")
    case "string":
        fmt.Println("Processing string")
    default:
        fmt.Printf("Processing %s\n", typeName)
    }
}

func WhetherCidrContainsIp

func WhetherCidrContainsIp(cidr, ip string) (bool, error)

WhetherCidrContainsIp checks if an IP address is within a CIDR range.

This function parses a CIDR notation string and determines whether the specified IP address falls within that network range.

Parameters:

  • cidr: CIDR notation string (e.g., "192.168.1.0/24", "10.0.0.0/8")
  • ip: IP address string (e.g., "192.168.1.100", "10.0.0.1")

Returns:

  • bool: true if IP is in CIDR range, false otherwise
  • error: Error if CIDR parsing fails

Example:

contains, err := utility.WhetherCidrContainsIp("192.168.1.0/24", "192.168.1.100")
if err != nil {
    log.Fatal(err)
}

if contains {
    fmt.Println("IP is in range")
} else {
    fmt.Println("IP is not in range")
}

Example with error handling:

cidr := "10.0.0.0/8"
ip := "10.5.100.200"

contains, err := utility.WhetherCidrContainsIp(cidr, ip)
if err != nil {
    fmt.Printf("Invalid CIDR: %v\n", err)
    return
}

fmt.Printf("%s is in %s: %v\n", ip, cidr, contains)

Types

type CallerInfo

type CallerInfo struct {
	PackageName  string
	FileName     string
	FunctionName string
	Line         int
	GoroutineID  int
}

CallerInfo is a GetCallerInfo that has caller information.

func GetCallerInfo

func GetCallerInfo(numberOfStackFramesToAscend int) (CallerInfo, error)

GetCallerInfo retrieves information about the calling function.

This function uses runtime introspection to gather details about the function that called it, including package, file, function name, line number, and goroutine ID.

Parameters:

  • numberOfStackFramesToAscend: Number of stack frames to skip 0 = GetCallerInfo itself 1 = Direct caller of GetCallerInfo 2 = Caller's caller, etc.

Returns:

  • CallerInfo: Struct containing caller details
  • error: Error if stack frame retrieval fails

The returned CallerInfo contains:

  • PackageName: Full package path (e.g., "github.com/user/project/pkg")
  • FileName: Base file name (e.g., "main.go")
  • FunctionName: Function name (e.g., "main" or "(*Type).Method")
  • Line: Line number
  • GoroutineID: ID of the current goroutine

Example:

func myFunction() {
    callerInfo, err := utility.GetCallerInfo(1)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Called from: %s\n", callerInfo.FileName)
    fmt.Printf("Line: %d\n", callerInfo.Line)
    fmt.Printf("Function: %s\n", callerInfo.FunctionName)
    fmt.Printf("Goroutine: %d\n", callerInfo.GoroutineID)
}

Example in logging:

func logWithCaller(message string) {
    info, _ := utility.GetCallerInfo(1)
    log.Printf("[%s:%d] %s", info.FileName, info.Line, message)
}

Example for debugging:

func debugStack() {
    for i := 0; i < 5; i++ {
        info, err := utility.GetCallerInfo(i)
        if err != nil {
            break
        }
        fmt.Printf("#%d %s:%d %s\n", i, info.FileName, info.Line, info.FunctionName)
    }
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL