Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/daze/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import (
"time"

"github.com/libraries/daze"
"github.com/libraries/daze/lib/doa"
"github.com/libraries/daze/lib/gracefulexit"
"github.com/libraries/daze/lib/rate"
"github.com/libraries/daze/protocol/ashe"
"github.com/libraries/daze/protocol/baboon"
"github.com/libraries/daze/protocol/czar"
"github.com/libraries/daze/protocol/dahlia"
"github.com/libraries/go/doa"
"github.com/libraries/go/gracefulexit"
"github.com/libraries/go/rate"
)

// Conf is acting as package level configuration.
Expand Down
10 changes: 5 additions & 5 deletions daze.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ import (
"sync"
"time"

"github.com/libraries/go/doa"
"github.com/libraries/go/lru"
"github.com/libraries/go/pretty"
"github.com/libraries/go/rate"
"github.com/libraries/daze/lib/doa"
"github.com/libraries/daze/lib/lru"
"github.com/libraries/daze/lib/pretty"
"github.com/libraries/daze/lib/rate"
)

// ============================================================================
Expand Down Expand Up @@ -1209,7 +1209,7 @@ func LoadApnic() map[string][]*net.IPNet {
log.Println("main: load apnic data from", url)
rep := doa.Try(http.Get(url))
defer rep.Body.Close()
f := io.TeeReader(rep.Body, pretty.NewProgressWriter(rep.ContentLength))
f := io.TeeReader(rep.Body, pretty.NewProgressWriter(uint64(rep.ContentLength)))
r := map[string][]*net.IPNet{}
s := bufio.NewScanner(f)
for s.Scan() {
Expand Down
2 changes: 1 addition & 1 deletion daze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"os/exec"
"testing"

"github.com/libraries/go/doa"
"github.com/libraries/daze/lib/doa"
)

const (
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module github.com/libraries/daze

go 1.25.0

require github.com/libraries/go v1.0.5
7 changes: 7 additions & 0 deletions lib/doa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Doa

Package doa is the abbreviation of the "Dead or alive". It provides some easy ways to make you panic down the program.

> One of the benefits of detecting problems as soon as you can is that you can crash earlier, and crashing is often the bet thing you can do. The alternative may be to continue, writing corrupted data to some vital database or commanding the washing machine into its twentieth consecutive spin cycle.

Crash is not poison, it's "Quit gracefully".
38 changes: 38 additions & 0 deletions lib/doa/doa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Package doa stands for "dead or alive". It provides simple utilities to intentionally crash the program with a panic.
package doa

import (
"log"
)

// Doa checks a boolean condition and triggers a panic if it’s false.
func Doa(b bool) {
if !b {
log.Panicln("doa: unreachable")
}
}

// Err returns the error passed to it, ignoring the first argument.
func Err(a any, err error) error {
return err
}

// Nil checks if an error is non-nil and panics if it is.
func Nil(err error) {
if err != nil {
log.Panicln("doa:", err)
}
}

// Try returns a value if there’s no error, otherwise it panics.
func Try[T any](a T, err error) T {
if err != nil {
log.Panicln("doa:", err)
}
return a
}

// Val returns the first argument, ignoring the error.
func Val[T any](a T, err error) T {
return a
}
3 changes: 3 additions & 0 deletions lib/gracefulexit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Gracefulexit

Package gracefulexit provides a method to exit the program gracefully. A graceful exit (or graceful handling) is a simple programming idiom[citation needed] wherein a program detects a serious error condition and "exits gracefully" in a controlled manner as a result. Often the program prints a descriptive error message to a terminal or log as part of the graceful exit.
27 changes: 27 additions & 0 deletions lib/gracefulexit/cmd/http/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"log"
"net"
"net/http"

"github.com/libraries/daze/lib/gracefulexit"
)

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
w.Write([]byte("\n"))
})
log.Println("main: listen and server on 127.0.0.1:8080")
l, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Panicln("main:", err)
}
server := http.Server{}
go server.Serve(l)
gracefulexit.Wait()
log.Println("main: server close")
server.Close()
log.Println("main: done")
}
22 changes: 22 additions & 0 deletions lib/gracefulexit/gracefulexit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Package gracefulexit provides a method to exit the program gracefully. A graceful exit (or graceful handling) is a
// simple programming idiom[citation needed] wherein a program detects a serious error condition and "exits gracefully"
// in a controlled manner as a result. Often the program prints a descriptive error message to a terminal or log as part
// of the graceful exit.
package gracefulexit

import (
"os"
"os/signal"
)

// Chan create a channel for os.Signal.
func Chan() chan os.Signal {
buffer := make(chan os.Signal, 1)
signal.Notify(buffer, os.Interrupt)
return buffer
}

// Wait for a signal.
func Wait() {
<-Chan()
}
3 changes: 3 additions & 0 deletions lib/lru/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Lru

Package lru implements an LRU cache.
146 changes: 146 additions & 0 deletions lib/lru/lru.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Package lru implements an LRU cache.
package lru

import (
"sync"
)

// Elem is an element of a linked list.
type Elem[K comparable, V any] struct {
Next, Prev *Elem[K, V]
K K
V V
}

// List represents a doubly linked list.
type List[K comparable, V any] struct {
Root *Elem[K, V]
Size int
}

// Init initializes or clears list l.
func (l *List[K, V]) Init() *List[K, V] {
root := Elem[K, V]{}
root.Prev = &root
root.Next = &root
l.Root = &root
l.Size = 0
return l
}

// Insert inserts e after root, increments l's size, and returns e.
func (l *List[K, V]) Insert(e *Elem[K, V]) *Elem[K, V] {
e.Prev = l.Root
e.Next = l.Root.Next
e.Prev.Next = e
e.Next.Prev = e
l.Size++
return e
}

// Remove removes e from its list, decrements l's size.
func (l *List[K, V]) Remove(e *Elem[K, V]) {
e.Prev.Next = e.Next
e.Next.Prev = e.Prev
e.Prev = nil // Avoid memory leaks
e.Next = nil // Avoid memory leaks
l.Size--
}

// Update e to next to root.
func (l *List[K, V]) Update(e *Elem[K, V]) {
if l.Root.Next == e {
return
}
e.Prev.Next = e.Next
e.Next.Prev = e.Prev
e.Prev = l.Root
e.Next = l.Root.Next
e.Prev.Next = e
e.Next.Prev = e
}

// Lru cache. It is safe for concurrent access.
type Lru[K comparable, V any] struct {
// Drop is called automatically when an elem is deleted.
Drop func(k K, v V)
// Size is the maximum number of cache entries before an item is evicted. Zero means no limit.
Size int
List *List[K, V]
C map[K]*Elem[K, V]
M *sync.Mutex
}

// Del removes the provided key from the cache.
func (l *Lru[K, V]) Del(k K) {
l.M.Lock()
defer l.M.Unlock()
if e, ok := l.C[k]; ok {
l.Drop(k, e.V)
delete(l.C, k)
l.List.Remove(e)
}
}

// Get looks up a key's value from the cache.
func (l *Lru[K, V]) GetExists(k K) (v V, ok bool) {
l.M.Lock()
defer l.M.Unlock()
var e *Elem[K, V]
e, ok = l.C[k]
if ok {
l.List.Update(e)
v = e.V
}
return
}

// Get looks up a key's value from the cache.
func (l *Lru[K, V]) Get(k K) (v V) {
v, _ = l.GetExists(k)
return
}

// Has returns true if a key exists.
func (l *Lru[K, V]) Has(k K) bool {
l.M.Lock()
defer l.M.Unlock()
_, b := l.C[k]
return b
}

// Len returns the number of items in the cache.
func (l *Lru[K, V]) Len() int {
l.M.Lock()
defer l.M.Unlock()
return l.List.Size
}

// Set adds a value to the cache.
func (l *Lru[K, V]) Set(k K, v V) {
l.M.Lock()
defer l.M.Unlock()
if e, ok := l.C[k]; ok {
l.List.Update(e)
e.K = k
e.V = v
return
}
if l.List.Size == l.Size {
l.Drop(l.List.Root.Prev.K, l.List.Root.Prev.V)
delete(l.C, l.List.Root.Prev.K)
l.List.Remove(l.List.Root.Prev)
}
l.C[k] = l.List.Insert(&Elem[K, V]{K: k, V: v})
}

// New returns a new LRU cache. If size is zero, the cache has no limit.
func New[K comparable, V any](size int) *Lru[K, V] {
return &Lru[K, V]{
Drop: func(k K, v V) {},
Size: size,
List: new(List[K, V]).Init(),
C: map[K]*Elem[K, V]{},
M: &sync.Mutex{},
}
}
74 changes: 74 additions & 0 deletions lib/lru/lru_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package lru

import (
"testing"
)

func TestLruAppend(t *testing.T) {
c := New[int, int](4)
c.Set(1, 1)
c.Set(2, 2)
c.Set(3, 3)
c.Set(4, 4)
c.Set(5, 5)
if c.Get(1) != 0 {
t.FailNow()
}
if c.Get(5) != 5 {
t.FailNow()
}
}

func TestLruChange(t *testing.T) {
c := New[int, int](4)
c.Set(1, 1)
c.Set(2, 2)
c.Set(3, 3)
c.Set(4, 4)
c.Set(1, 5)
if c.Get(1) != 5 {
t.FailNow()
}
}

func TestLruDel(t *testing.T) {
c := New[int, int](4)
c.Set(1, 1)
c.Set(2, 2)
c.Set(3, 3)
c.Set(4, 4)
c.Del(2)
if c.List.Size != c.Len() || c.Len() != 3 {
t.FailNow()
}
if c.Get(2) != 0 {
t.FailNow()
}
}

func TestLruSize(t *testing.T) {
c := New[int, int](4)
if c.List.Size != c.Len() || c.Len() != 0 {
t.FailNow()
}
c.Set(1, 1)
if c.List.Size != c.Len() || c.Len() != 1 {
t.FailNow()
}
c.Set(2, 2)
if c.List.Size != c.Len() || c.Len() != 2 {
t.FailNow()
}
c.Set(3, 3)
if c.List.Size != c.Len() || c.Len() != 3 {
t.FailNow()
}
c.Set(4, 4)
if c.List.Size != c.Len() || c.Len() != 4 {
t.FailNow()
}
c.Set(5, 5)
if c.List.Size != c.Len() || c.Len() != 4 {
t.FailNow()
}
}
Loading