Skip to content

Commit

Permalink
Desafio 07 em Go by MatMercer (#965)
Browse files Browse the repository at this point in the history
  • Loading branch information
MatMercer authored Aug 13, 2023
1 parent 48a99b8 commit 76b6952
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
33 changes: 33 additions & 0 deletions desafio-07/MatMercer/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Solução desafio 7 unix tac em Go

Executar:

```bash
go build tac.go
./tac 1GB.txt > out.txt
md5sum out.txt
2b4fd25f11d75c285ec69ecac420bd07 out.txt
```

Para testar os 512MB de memória com control groups v1:

```bash
# install cgroup
sudo apt-get install cgroup-tools

# setup cgroup
export CGROUP=osProgramadoresD7
export CGROUPP="memory/$CGROUP"
sudo cgcreate -t $USER:$USER -a $USER:$USER -g memory:"$CGROUP"
echo 512M > "/sys/fs/cgroup/$CGROUPP/memory.limit_in_bytes"
echo 0 > "/sys/fs/cgroup/$CGROUPP/memory.swappiness"

# run in cgroup
cgexec -g memory:$CGROUP ./tac 1GB.txt > out.txt
md5sum out.txt
2b4fd25f11d75c285ec69ecac420bd07 out.txt

# check max mem used
cat "/sys/fs/cgroup/$CGROUPP/memory.max_usage_in_bytes" | numfmt --to=iec
512M
```
139 changes: 139 additions & 0 deletions desafio-07/MatMercer/go/tac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package main

import (
"bufio"
"bytes"
"io"
"log"
"os"
)

// in MB size, currently the program uses a max of 2*bufSize iff file size > bufSize
const maxBufSize = int64(250 << (10 * 2))

var stdout *bufio.Writer

// min standard math.Min supports only float64
func min(x int64, y int64) int64 {
if x > y {
return y
}
return x
}

type ReverseReader struct {
f *os.File
offset int64
}

func (r *ReverseReader) Read(b []byte) (n int, err error) {
r.offset -= int64(len(b))
_, err = r.f.Seek(r.offset, io.SeekStart)
if err != nil {
return 0, err
}
read, err := r.f.Read(b)
if err != nil {
return 0, err
}
return read, nil
}

func tac(fileName string) error {
f, err := os.Open(fileName)
if err != nil {
return err
}
fi, err := f.Stat()
if err != nil {
return err
}

// buffers stdout by 128kb
stdout = bufio.NewWriterSize(os.Stdout, 128<<(10))

fileSize := fi.Size()
bufSize := min(fileSize, maxBufSize)
readBuf := make([]byte, bufSize)
maxRead := bufSize
start := fileSize
lineAcc := bytes.NewBuffer([]byte{})
r := &ReverseReader{f, fileSize}
for start != 0 {
start -= bufSize
if start < 0 {
// prevent over reading if result is less than buf size
maxRead += start
start = 0
}

_, err = r.Read(readBuf[:maxRead])
if err != nil {
return err
}

// search until backwards \n and prints it
lastEnd := maxRead
for i := maxRead - 1; i >= 0; i-- {
if readBuf[i] == '\n' {
// write everything but '\n', since readBuf[i] == '\n'
_, err = stdout.Write(readBuf[i+1 : lastEnd])
if err != nil {
return err
}
// writes accumulated value
_, err = stdout.Write(lineAcc.Bytes())
if err != nil {
return err
}
// reset the accumulator to receive next line
lineAcc.Reset()
// makes '\n' be printed in next iteration
lastEnd = i + 1
}

// on last iteration, create a new accumulator with [max-read][current-accumulator]
if i == 0 {
newAcc := bytes.NewBuffer(nil)
_, err = newAcc.Write(readBuf[i:lastEnd])
if err != nil {
return err
}
_, err = newAcc.Write(lineAcc.Bytes())
if err != nil {
return err
}
lineAcc = newAcc
}
}
}
// closes the file
_ = f.Close()

// prints last chunk of data
// fmt.Println is unbuffered https://github.com/golang/go/issues/36619
_, err = stdout.Write(lineAcc.Bytes())
if err != nil {
return err
}

err = stdout.Flush()
if err != nil {
return err
}

return nil
}

func main() {
logger := log.New(os.Stderr, "", 0)
if len(os.Args) != 2 {
logger.Fatalln("usage: tac [file]")
}

fileName := os.Args[1]
err := tac(fileName)
if err != nil {
logger.Fatalf("tac error: %v", err)
}
}

0 comments on commit 76b6952

Please sign in to comment.