-
Notifications
You must be signed in to change notification settings - Fork 0
/
urlstore.go
127 lines (105 loc) · 2.38 KB
/
urlstore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"log"
"os"
"sync"
)
const saveQueueLength = 1000
// URLStore is a map that stores URLs and their shortened forms.
type URLStore struct {
urls map[string]string
mu sync.RWMutex
saver chan record
}
// URLStore records
type record struct {
Key, URL string
}
// NewURLStore creates a new URLStore.
func NewURLStore(filename string) *URLStore {
store := &URLStore{
urls: make(map[string]string),
saver: make(chan record, saveQueueLength),
}
// load the file
if err := store.load(filename); err != nil {
log.Println("Error loading data store:", err)
}
go store.saveLoop(filename)
return store
}
// Get returns the URL to which the given short URL maps.
func (s *URLStore) Get(key string) string {
s.mu.RLock()
defer s.mu.RUnlock()
return s.urls[key]
}
// Set saves the given URL to the store and returns the corresponding short URL.
func (s *URLStore) Set(key, url string) bool {
s.mu.Lock()
defer s.mu.Unlock()
// Check if the URL is already in the store.
if _, present := s.urls[key]; present {
return false
}
s.urls[key] = url
return true
}
func (s *URLStore) Count() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.urls)
}
func (s *URLStore) Put(url string) (string, error) {
key := genKey(url)
if ok := s.Set(key, url); !ok {
return "", fmt.Errorf("error setting key in URLStore")
}
s.saver <- record{key, url}
return key, nil
}
// Saves records to file
func (s *URLStore) saveLoop(filename string) {
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
if err != nil {
log.Fatal("Error opening URLStore: ", err)
}
defer f.Close()
encoder := json.NewEncoder(f)
for {
record := <-s.saver
if err := encoder.Encode(record); err != nil {
log.Println("Error saving to URLStore: ", err)
}
}
}
func (s *URLStore) load(filename string) error {
f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDONLY, 0644)
if err != nil {
log.Fatal("Error opening URLStore: ", err)
}
defer f.Close()
if _, err = f.Seek(0, 0); err != nil {
return err
}
d := json.NewDecoder(f)
//var err error
for err == nil {
var r record
if err = d.Decode(&r); err == nil {
s.Set(r.Key, r.URL)
}
}
if err == io.EOF {
return nil
}
return err
}
func genKey(url string) string {
// takes hash of the key
return fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
}