Skip to content

Commit

Permalink
Read only last 16Kb of file
Browse files Browse the repository at this point in the history
  • Loading branch information
basbossink committed Nov 10, 2021
1 parent 54d6ab6 commit c715b3a
Showing 1 changed file with 113 additions and 52 deletions.
165 changes: 113 additions & 52 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
)

const (
timeEncodedSize = 4
bufSize = 16 * 1024
sunDataDir = ".sun.d"
sunDataFileTimestampFormat = "2006"
Expand Down Expand Up @@ -58,57 +57,90 @@ func (reader *entryReader) Read() (*entry, error) {
if len(reader.toProcess) == 0 {
return &entry{}, io.EOF
}
sizeLen := reader.toProcess[len(reader.toProcess)-1]
sansLen := reader.toProcess[:len(reader.toProcess)-1]
varintStart := len(sansLen) - int(sizeLen)
varintSlice := sansLen[varintStart:]
dec, n := binary.Uvarint(varintSlice)
sizeSizePos := len(reader.toProcess) - 1
sizeSize := reader.toProcess[sizeSizePos]
sansSizeSize := reader.toProcess[:sizeSizePos]
varintStart := len(sansSizeSize) - int(sizeSize)
if varintStart < 0 {
return &entry{}, io.EOF
}
gobStart, err := readGobStart(sansSizeSize, varintStart)
if err != nil {
return &entry{}, err
}
if gobStart < 0 {
return &entry{}, io.EOF
}
sansSize := sansSizeSize[gobStart:varintStart]
result, err := decode(sansSize)
reader.toProcess = reader.toProcess[:gobStart]
return result, err
}

func readGobStart(slice []byte, varIntStart int) (int, error) {
slice = slice[varIntStart:]
dec, n := binary.Uvarint(slice)
if n <= 0 {
return &entry{}, ErrFileCorrupt
return 0, ErrFileCorrupt
}
sansSize := sansLen[varintStart-int(dec) : varintStart]
rr := bytes.NewReader(sansSize)
gobStart := varIntStart - int(dec)
return gobStart, nil
}

func decode(slice []byte) (*entry, error) {
rr := bytes.NewReader(slice)
decoder := gob.NewDecoder(rr)
var result entry
err := decoder.Decode(&result)
if err != nil {
return &entry{}, err
}
reader.toProcess = reader.toProcess[:len(reader.toProcess)-int(dec)-n-1]
return &result, nil
}

func (entry *entry) Write(w io.Writer) error {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(entry)
size, err := writeGob(&buf, entry)
if err != nil {
return err
}
size := uint64(buf.Len())
lenBuf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(lenBuf, size)
reduced := lenBuf[:n]
if written, errwr := buf.Write(reduced); errwr != nil {
return errwr
} else if written != n {
return ErrInsufficientSize
}
if written, errwr := buf.Write([]byte{byte(n)}); errwr != nil {
return errwr
} else if written != 1 {
return ErrInsufficientSize
}
written, err := w.Write(buf.Bytes())
n, err := writeSize(&buf, size)
if err != nil {
return err
}
if written == 0 {
if err := writeSizeSize(&buf, n); err != nil {
return err
}
_, err = w.Write(buf.Bytes())
return err
}

func writeSizeSize(buf *bytes.Buffer, sizeSize int) error {
if written, errwr := buf.Write([]byte{byte(sizeSize)}); errwr != nil {
return errwr
} else if written != 1 {
return ErrInsufficientSize
}
return nil
}

func writeSize(buf *bytes.Buffer, size uint64) (int, error) {
lenBuf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(lenBuf, size)
reduced := lenBuf[:n]
if _, errwr := buf.Write(reduced); errwr != nil {
return 0, errwr
}
return n, nil
}

func writeGob(buf *bytes.Buffer, entry *entry) (uint64, error) {
enc := gob.NewEncoder(buf)
err := enc.Encode(entry)
size := uint64(buf.Len())
return size, err
}

func ensureDataDir() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
Expand All @@ -122,59 +154,88 @@ func ensureDataDir() (string, error) {
return dataDir, nil
}

func printLastEntries(dataDir string) {
func openDataFile(dataDir string) *os.File {
filename := calculateFilename(dataDir)
f, err := os.OpenFile(filename, os.O_RDONLY, 0600)
if err != nil {
fmt.Errorf("could not open file %w\n", err)
log.Fatalf("could not open file %v", err)
}
fileInf, errrr := f.Stat()
if errrr != nil {
log.Fatalf("could not stat file %v", errrr)
}
if bufSize < fileInf.Size() {
_, errr := f.Seek(-1*bufSize, io.SeekEnd)
if errr != nil {
log.Fatalf("could not seek in file %v", errr)
}
}
return f
}

func printLastEntries(dataDir string) {
f := openDataFile(dataDir)
defer f.Close()
er, err := NewReader(f)
if err != nil {
fmt.Errorf("could not create entry reader %w\n", err)
log.Fatalf("could not create entry reader %v", err)
}
writeTable(er)
}

func writeTable(er *entryReader) {
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', tabwriter.Debug)
prevDate := ""
dayCounter := 0
for entry, err := er.Read(); err != io.EOF && dayCounter < 2; entry, err = er.Read() {
curDate := entry.CreatedAt.Format(dateFormat)
if prevDate == "" {
prevDate = curDate
}
if prevDate != curDate {
fmt.Fprintln(w, dateDivider)
dayCounter++
}
fmt.Fprintln(
w,
fmt.Sprintf(
rowFormat,
entry.CreatedAt.Format(weekdayFormat),
curDate,
entry.CreatedAt.Format(timeFormat),
strings.Join(entry.Tags, " "),
entry.Note))
prevDate = curDate
prevDate, dayCounter = writeRow(w, entry, prevDate, dayCounter)
}
w.Flush()
}

func writeRow(w *tabwriter.Writer, entry *entry, prevDate string, dayCount int) (string, int) {
nextDayCount := dayCount
curDate := entry.CreatedAt.Format(dateFormat)
if prevDate == "" {
prevDate = curDate
}
if prevDate != curDate {
fmt.Fprintln(w, dateDivider)
nextDayCount++
}
fmt.Fprintln(
w,
fmt.Sprintf(
rowFormat,
entry.CreatedAt.Format(weekdayFormat),
curDate,
entry.CreatedAt.Format(timeFormat),
strings.Join(entry.Tags, " "),
entry.Note))
return curDate, nextDayCount
}

func writeNewEntry(dataDir string, args []string) {
filename := calculateFilename(dataDir)
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
fmt.Errorf("could not open file %w\n", err)
log.Fatalf("could not open file %f", err)
}
defer f.Close()
entry := convertArgsToEntry(args)
err = entry.Write(f)
if err != nil {
fmt.Errorf("could not write entry %w\n", err)
log.Fatalf("could not write entry %v", err)
}
}

func calculateFilename(dataDir string) string {
filename := filepath.Join(dataDir, fmt.Sprintf("%s%s", time.Now().Format(sunDataFileTimestampFormat), sunDataFileExtension))
filename := filepath.Join(
dataDir,
fmt.Sprintf(
"%s%s",
time.Now().Format(sunDataFileTimestampFormat),
sunDataFileExtension))
return filename
}

Expand Down

0 comments on commit c715b3a

Please sign in to comment.