Selaa lähdekoodia

Id generation.

Kelly Norton 10 vuotta sitten
vanhempi
sitoutus
ae1fbbd801
2 muutettua tiedostoa jossa 71 lisäystä ja 21 poistoa
  1. 42 12
      context/context.go
  2. 29 9
      web/web.go

+ 42 - 12
context/context.go

@@ -64,6 +64,39 @@ type Context struct {
 	id   uint64
 }
 
+func commit(filename string, id uint64) error {
+	w, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer w.Close()
+
+	if err := binary.Write(w, binary.LittleEndian, id); err != nil {
+		return err
+	}
+
+	return w.Sync()
+}
+
+func load(filename string) (uint64, error) {
+	if _, err := os.Stat(filename); err != nil {
+		return 0, commit(filename, idBatchSize)
+	}
+
+	r, err := os.Open(filename)
+	if err != nil {
+		return 0, err
+	}
+	defer r.Close()
+
+	var id uint64
+	if err := binary.Read(r, binary.LittleEndian, &id); err != nil {
+		return 0, err
+	}
+
+	return id, nil
+}
+
 // Open ...
 func Open(path string) (*Context, error) {
 	if _, err := os.Stat(path); err != nil {
@@ -78,19 +111,16 @@ func Open(path string) (*Context, error) {
 		return nil, err
 	}
 
-	c := &Context{
-		path: path,
-		db:   db,
-	}
-
-	// make sure we have an id log file
-	if _, err := os.Stat(filepath.Join(path, idLogFilename)); err != nil {
-		if err := c.commit(idBatchSize); err != nil {
-			return nil, err
-		}
+	id, err := load(filepath.Join(path, idLogFilename))
+	if err != nil {
+		return nil, err
 	}
 
-	return c, nil
+	return &Context{
+		path: path,
+		db:   db,
+		id:   id,
+	}, nil
 }
 
 // Get ...
@@ -141,7 +171,7 @@ func (c *Context) NextID() (uint64, error) {
 	// boundary. If we crash, we'll just throw away a batch of ids in the worst
 	// case.
 	if c.id%idBatchSize == 0 {
-		if err := c.commit(c.id + idBatchSize); err != nil {
+		if err := commit(filepath.Join(c.path, idLogFilename), c.id+idBatchSize); err != nil {
 			return 0, err
 		}
 	}

+ 29 - 9
web/web.go

@@ -2,12 +2,9 @@ package web
 
 import (
 	"bytes"
-	"encoding/base64"
-	"encoding/binary"
 	"encoding/json"
 	"fmt"
 	"log"
-	"math/rand"
 	"net/http"
 	"strings"
 	"time"
@@ -16,10 +13,23 @@ import (
 	"github.com/syndtr/goleveldb/leveldb"
 )
 
-func makeName() string {
-	var buf bytes.Buffer
-	binary.Write(&buf, binary.LittleEndian, rand.Int63())
-	return base64.URLEncoding.EncodeToString(buf.Bytes())
+const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+func encodeID(id uint64) string {
+	n := uint64(len(alpha))
+	b := make([]byte, 0, 8)
+	if id == 0 {
+		return "0"
+	}
+
+	b = append(b, ':')
+
+	for id > 0 {
+		b = append(b, alpha[id%n])
+		id /= n
+	}
+
+	return string(b)
 }
 
 func parseName(base, path string) string {
@@ -137,8 +147,13 @@ func (h *defaultHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	p := parseName("/", r.URL.Path)
 
 	if p == "" {
+		id, err := h.ctx.NextID()
+		if err != nil {
+			log.Panic(err)
+		}
+
 		http.Redirect(w, r,
-			fmt.Sprintf("/edit/%s", makeName()),
+			fmt.Sprintf("/edit/%s", encodeID(id)),
 			http.StatusTemporaryRedirect)
 		return
 	}
@@ -166,8 +181,13 @@ func (h *editHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	p := parseName("/edit/", r.URL.Path)
 
 	if p == "" {
+		id, err := h.ctx.NextID()
+		if err != nil {
+			log.Panic(err)
+		}
+
 		http.Redirect(w, r,
-			fmt.Sprintf("/edit/%s", makeName()),
+			fmt.Sprintf("/edit/%s", encodeID(id)),
 			http.StatusTemporaryRedirect)
 		return
 	}