Skip to content

Commit

Permalink
Add: channel impl, defer order, file TODO, decorator pattern, more sl…
Browse files Browse the repository at this point in the history
…ices, int overflow
  • Loading branch information
Petrus Breedt committed Sep 17, 2024
1 parent 62f940c commit ddbe88d
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 8 deletions.
120 changes: 120 additions & 0 deletions channels/urlpoller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package channels

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// ######
// Codewalk through: https://go.dev/doc/codewalk/sharemem/
// Premise: Share Memory by Communicating

import (
"log"
"net/http"
"time"
)

const (
numPollers = 2 // number of Poller goroutines to launch
pollInterval = 60 * time.Second // how often to poll each URL
statusInterval = 10 * time.Second // how often to log status to stdout
errTimeout = 10 * time.Second // back-off timeout on error
)

var urls = []string{
"http://www.google.com/",
"http://golang.org/",
"http://blog.golang.org/",
}

// State represents the last-known state of a URL.
type State struct {
url string
status string
}

// StateMonitor maintains a map that stores the state of the URLs being
// polled, and prints the current state every updateInterval nanoseconds.
// It returns a chan State to which resource state should be sent.
func StateMonitor(updateInterval time.Duration) chan<- State {
updates := make(chan State)
urlStatus := make(map[string]string)
ticker := time.NewTicker(updateInterval)
go func() {
for {
select {
case <-ticker.C:
logState(urlStatus)
case s := <-updates:
urlStatus[s.url] = s.status
}
}
}()
return updates
}

// logState prints a state map.
func logState(s map[string]string) {
log.Println("Current state:")
for k, v := range s {
log.Printf(" %s %s", k, v)
}
}

// Resource represents an HTTP URL to be polled by this program.
type Resource struct {
url string
errCount int
}

// Poll executes an HTTP HEAD request for url
// and returns the HTTP status string or an error string.
func (r *Resource) Poll() string {
resp, err := http.Head(r.url)
if err != nil {
log.Println("Error", r.url, err)
r.errCount++
return err.Error()
}
r.errCount = 0
return resp.Status
}

// Sleep sleeps for an appropriate interval (dependent on error state)
// before sending the Resource to done.
func (r *Resource) Sleep(done chan<- *Resource) {
time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
done <- r
}

func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {
for r := range in {
s := r.Poll()
status <- State{r.url, s}
out <- r
}
}

func main() {
// Create our input and output channels.
pending, complete := make(chan *Resource), make(chan *Resource)

// Launch the StateMonitor.
status := StateMonitor(statusInterval)

// Launch some Poller goroutines.
for i := 0; i < numPollers; i++ {
go Poller(pending, complete, status)
}

// Send some Resources to the pending queue.
go func() {
for _, url := range urls {
pending <- &Resource{url: url}
}
}()

for r := range complete {
go r.Sleep(pending)
}
}
12 changes: 12 additions & 0 deletions defer/defer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dfer

func basics() {
order()
}

func order() {
for i := 0; i < 5; i++ {
// defers executed in LIFO order
defer println(i)
}
}
7 changes: 7 additions & 0 deletions defer/defer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dfer

import "testing"

func TestBasics(t *testing.T) {
basics()
}
18 changes: 18 additions & 0 deletions files-TODO/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package files

import (
"fmt"
"os"
)

func Read(filename string) ([]byte, int, error) {
f, e := os.Open(filename)
if e != nil {
fmt.Printf("error opening file: %v", e)
return nil, 0, e
}

b := make([]byte, 10)
n, e := f.Read(b)
return b, n, e
}
16 changes: 16 additions & 0 deletions files-TODO/read_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package files

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestR(t *testing.T) {
b, n, e := Read("test-file.txt")
fmt.Printf("read %d bytes: %s (error=%v)", n, b, e)
assert.Equal(t, nil, e)
assert.Equal(t, []byte("abc\ndef\ngh"), b)
assert.Equal(t, 10, n)
}
9 changes: 9 additions & 0 deletions files-TODO/test-file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
abc
def
ghi
jkl
mno
pqr
stu
vwx
yz
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ module github.com/pbreedt/learn-go
go 1.18

require golang.org/x/sync v0.7.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
39 changes: 39 additions & 0 deletions patterns/decorator/reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package decorator

import "fmt"

type Reader interface {
Read() string
}

type FileReader struct{}

func (fr *FileReader) Read() string {
return "Read file..."
}

type CompressReader struct {
reader Reader
}

func (cr *CompressReader) Read() string {
file := cr.reader.Read()
return file + "Compress data..."
}

type EncryptReader struct {
reader Reader
}

func (er *EncryptReader) Read() string {
file := er.reader.Read()
return file + "Encrypting data..."
}

func main() {
fr := &FileReader{}
cr := &CompressReader{fr}
er := &EncryptReader{cr}

fmt.Println(er.Read())
}
17 changes: 17 additions & 0 deletions patterns/decorator/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package decorator

import (
"testing"
)

func TestReader(t *testing.T) {
fr := &FileReader{}
cr := &CompressReader{fr}
er := &EncryptReader{cr}

data := er.Read()
expect := "Read file...Compress data...Encrypting data..."
if data != expect {
t.Fatalf("Fail: expected:%s, got:%s", expect, data)
}
}
23 changes: 15 additions & 8 deletions slices/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ func basics() {
b := a //copy by assignment, ref same underlying array
c := make([]string, len(b))
copy(c, b) // copy after 'make', new underlying array
fmt.Println(a, b, c)
fmt.Println("1>", a, b, c)

b[0] = "XXX" // change to slice (even copy of slice) affects underlying array as well
c[1] = "OOO" // change to new slice (with make) does not affect underlying array as well
fmt.Println(a, b, c)
fmt.Println(names)
fmt.Println("2>", a, b, c)
fmt.Println("3>", names)

aa := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(aa[0:10]) // all the same
fmt.Println(aa[:10]) // all the same
fmt.Println(aa[0:]) // all the same
fmt.Println(aa[:]) // all the same
fmt.Println("4>", aa[0:10]) // all the same
fmt.Println("5>", aa[:10]) // all the same
fmt.Println("6>", aa[0:]) // all the same
fmt.Println("7>", aa[:]) // all the same
}

func lenAndCap() {
s := []int{2, 3, 5, 7, 11, 13}
s := []int{0, 1, 2, 3, 4, 5}
printSlice(s)

// Slice the slice to give it zero length.
Expand All @@ -56,6 +56,13 @@ func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

func Copy() {
data := []int{1, 2, 3, 4, 5}
cpy := make([]int, len(data))
// cpy = append(cpy, data...) // ERROR: make already initializes 5 elements with default values, so [0,0,0,0,0], append just adds to this array
copy(cpy, data) // CORRECT: copies data to new slice. NOTE: min length of len(data) and len(cpy) will be used
}

func RemoveIndexString(s []string, i int) []string {
newSlice := make([]string, 0) // create new empty slice
newSlice = append(newSlice, s[:i]...) // add all elements - up to element i
Expand Down
10 changes: 10 additions & 0 deletions types/int.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package types

import "fmt"

func overflow() int8 {
var max int8 = 127
// overflow, results in -128
fmt.Println("int8: max=", max, ", max + 1=", max+1)
return max + 1
}
10 changes: 10 additions & 0 deletions types/int_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package types

import "testing"

func TestOverflow(t *testing.T) {
of := overflow()
if of != -128 {
t.Fail()
}
}

0 comments on commit ddbe88d

Please sign in to comment.