summaryrefslogtreecommitdiff
path: root/store/page.go
blob: f654174fac742e629e585f17358b73cfe6352a96 (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package store

import "C"
import (
	"encoding/binary"
	"fmt"
	log "github.com/sirupsen/logrus"
	"github.com/syndtr/goleveldb/leveldb"
	"os"
)

const pageSize = 64

// pageDB returns leveldb of page variant and creates it as required.
func (s *Store) pageDB(variant string) *leveldb.DB {
	if s.pageldb[variant] != nil {
		return s.pageldb[variant]
	} else {
		if db, err := leveldb.OpenFile(s.PageVariantPath(variant), nil); err != nil {
			s.fatalClose(fmt.Sprintf("Error opening leveldb for page variant %s, %s", variant, err))
		} else {
			s.pageldb[variant] = db
			if _, err = db.Get([]byte("\000"), nil); err != nil {
				log.Infof("Page variant %s created.", variant)
				s.pageSetTotalCount(variant, 0)
			}
			return db
		}
	}
	return nil
}

// pageDBDestroy destroys leveldb of page variant.
func (s *Store) pageDBDestroy(variant string) {
	if err := s.pageDB(variant).Close(); err != nil {
		s.fatalClose(fmt.Sprintf("Error closing leveldb for page variant %s, %s", variant, err))
	} else {
		delete(s.pageldb, variant)
	}

	if err := os.RemoveAll(s.PageVariantPath(variant)); err != nil {
		s.fatalClose(fmt.Sprintf("Error destroying page variant %s, %s", variant, err))
	}

	log.Infof("Page variant %s destroyed.", variant)
}

// pageGetTotalCount gets total count of a page variant.
func (s *Store) pageGetTotalCount(variant string) uint64 {
	db := s.pageDB(variant)

	if payload, err := db.Get([]byte("\000"), nil); err != nil {
		s.fatalClose(fmt.Sprintf("Error getting page variant %s total count, %s", variant, err))
	} else {
		return binary.LittleEndian.Uint64(payload)
	}

	return 0
}

// pageSetTotalCount sets total count of a page variant.
func (s *Store) pageSetTotalCount(variant string, value uint64) {
	db := s.pageDB(variant)

	payload := make([]byte, 8)
	binary.LittleEndian.PutUint64(payload, value)

	if err := db.Put([]byte("\000"), payload, nil); err != nil {
		s.fatalClose(fmt.Sprintf("Error setting page variant %s total count, %s", variant, err))
	}
}

// pageAdvanceTotalCount advances total count of a page variant.
func (s *Store) pageAdvanceTotalCount(variant string) {
	s.pageSetTotalCount(variant, s.pageGetTotalCount(variant)+1)
}

// pageReduceTotalCount reduces total count of a page variant.
func (s *Store) pageReduceTotalCount(variant string) {
	s.pageSetTotalCount(variant, s.pageGetTotalCount(variant)-1)
}

// PageTotal returns total amount of pages.
func (s *Store) PageTotal(variant string) int {
	return (int(s.pageGetTotalCount(variant)) / pageSize) + 1
}

// Page returns all entries in a page.
func (s *Store) Page(variant string, entry int) []string {
	if entry >= s.PageTotal(variant) {
		return nil
	}

	var page []string
	start := entry*pageSize + 1
	end := start + pageSize
	begin := false

	db := s.pageDB(variant)

	iter := db.NewIterator(nil, nil)
	i := 0
	for iter.Next() {
		if i == end {
			break
		}
		if begin {
			page = append(page, string(iter.Key()))
		} else {
			if i >= start {
				begin = true
			}
		}
		i++
	}
	iter.Release()
	if err := iter.Error(); err != nil {
		log.Warnf("Error iterating page variant %s entry %v, %s", variant, entry, err)
		return nil
	}

	return page
}

// PageImages returns all images in a page.
func (s *Store) PageImages(variant string, entry int) []Image {
	flakes := s.Page(variant, entry)
	if flakes == nil {
		return nil
	}

	images := make([]Image, len(flakes))
	for _, flake := range flakes {
		image := s.ImageSnowflake(flake)
		if image.Snowflake == flake {
			images = append(images, image)
		}
	}
	return images
}

// PageInsert inserts an image into the index.
func (s *Store) PageInsert(variant, flake string) {
	if !s.dir(s.ImageSnowflakePath(flake)) {
		return
	}

	s.getLock("page_" + variant).Lock()
	defer s.getLock("page_" + variant).Unlock()

	db := s.pageDB(variant)
	if err := db.Put([]byte(flake), []byte{}, nil); err != nil {
		s.fatalClose(fmt.Sprintf("Error inserting image %s into page variant %s, %s", flake, variant, err))
	}
	s.pageAdvanceTotalCount(variant)
}

// PageRegisterRemove registers an image remove.
func (s *Store) PageRegisterRemove(variant, flake string) {
	s.getLock("page_" + variant).Lock()
	defer s.getLock("page_" + variant).Unlock()

	db := s.pageDB(variant)
	if err := db.Delete([]byte(flake), nil); err != nil {
		s.fatalClose(fmt.Sprintf("Error removing image %s from page variant %s, %s", flake, variant, err))
	}
	s.pageReduceTotalCount(variant)
}