record.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. /*
  11. Package file deals with low level file storage and transaction management.
  12. StorageFile
  13. StorageFile models a logical storage file which stores fixed size records on
  14. disk. Each record has a unique record id. On disk this logical storage file
  15. might be split into several smaller files. StorageFiles can be reused after
  16. they were closed if the transaction management has been disabled. This is
  17. not the case otherwise.
  18. Record
  19. A record is a byte slice of a StorageFile. It is a wrapper data structure for
  20. a byte array which provides read and write methods for several data types.
  21. TransactionManager
  22. TransactionManager provides an optional transaction management for StorageFile.
  23. When used each record which is released from use is added to an in memory
  24. transaction log. Once the client calls Flush() on the StorageFile the
  25. in memory transaction is written to a transaction log on disk. The in-memory log
  26. is kept. The in-memory transaction log is written to the actual StorageFile once
  27. maxTrans is reached or the StorageFile is closed.
  28. Should the process crash during a transaction, then the transaction log is
  29. written to the StorageFile on the next startup using the recover() function.
  30. */
  31. package file
  32. import (
  33. "bytes"
  34. "encoding/binary"
  35. "fmt"
  36. "io"
  37. "devt.de/krotik/common/bitutil"
  38. "devt.de/krotik/common/stringutil"
  39. )
  40. /*
  41. Size constants for a record
  42. */
  43. const (
  44. SizeByte = 1
  45. SizeUnsignedShort = 2
  46. SizeShort = 2
  47. SizeThreeByteInt = 3
  48. SizeUnsignedInt = 4
  49. SizeInt = 4
  50. SizeSixByteLong = 6
  51. SizeLong = 8
  52. )
  53. /*
  54. Record data structure
  55. */
  56. type Record struct {
  57. id uint64 // 64-bit record id
  58. data []byte // Slice of the whole data byte array
  59. dirty bool // Firty flag to indicate change
  60. transCount int // Transaction counter
  61. pageView interface{} // View on this record (this is not persisted)
  62. }
  63. /*
  64. NewRecord creates a new Record and returns a pointer to it.
  65. */
  66. func NewRecord(id uint64, data []byte) *Record {
  67. return &Record{id, data, false, 0, nil}
  68. }
  69. /*
  70. ID returns the id of a Record.
  71. */
  72. func (r *Record) ID() uint64 {
  73. return r.id
  74. }
  75. /*
  76. SetID changes the id of a Record.
  77. */
  78. func (r *Record) SetID(id uint64) error {
  79. if r.InTransaction() {
  80. return fmt.Errorf("Record id cannot be changed. Record "+
  81. "is used in %d transaction%s.", r.transCount,
  82. stringutil.Plural(r.transCount))
  83. }
  84. r.id = id
  85. return nil
  86. }
  87. /*
  88. Data returns the raw data of a Record.
  89. */
  90. func (r *Record) Data() []byte {
  91. return r.data
  92. }
  93. /*
  94. Dirty returns the dirty flag of a Record.
  95. */
  96. func (r *Record) Dirty() bool {
  97. return r.dirty
  98. }
  99. /*
  100. SetDirty sets the dirty flag of a Record.
  101. */
  102. func (r *Record) SetDirty() {
  103. r.dirty = true
  104. }
  105. /*
  106. ClearDirty clears the dirty flag of a Record.
  107. */
  108. func (r *Record) ClearDirty() {
  109. r.dirty = false
  110. }
  111. /*
  112. ClearData removes all stored data from a Record.
  113. */
  114. func (r *Record) ClearData() {
  115. var ccap, clen int
  116. if r.data != nil {
  117. ccap = cap(r.data)
  118. clen = len(r.data)
  119. } else {
  120. clen = DefaultRecordSize
  121. ccap = DefaultRecordSize
  122. }
  123. r.data = make([]byte, clen, ccap)
  124. r.ClearDirty()
  125. }
  126. /*
  127. InTransaction returns if the Record is used in a transaction.
  128. */
  129. func (r *Record) InTransaction() bool {
  130. return r.transCount != 0
  131. }
  132. /*
  133. IncTransCount increments the transaction count which means the record is in the
  134. log but not yet in the data file.
  135. */
  136. func (r *Record) IncTransCount() {
  137. r.transCount++
  138. }
  139. /*
  140. DecTransCount decrements the transaction count which means the record has been
  141. written to disk.
  142. */
  143. func (r *Record) DecTransCount() {
  144. r.transCount--
  145. if r.transCount < 0 {
  146. panic(fmt.Sprintf("Transaction count for record %v is below zero: %v",
  147. r.id, r.transCount))
  148. }
  149. }
  150. /*
  151. PageView returns the view on this record. The view determines how the record
  152. is being used.
  153. */
  154. func (r *Record) PageView() interface{} {
  155. return r.pageView
  156. }
  157. /*
  158. SetPageView sets the view on this record.
  159. */
  160. func (r *Record) SetPageView(view interface{}) {
  161. r.pageView = view
  162. }
  163. /*
  164. String prints a string representation the Record.
  165. */
  166. func (r *Record) String() string {
  167. return fmt.Sprintf("Record: %v (dirty:%v transCount:%v len:%v cap:%v)\n%v",
  168. r.id, r.dirty, r.transCount, len(r.data), cap(r.data), bitutil.HexDump(r.data))
  169. }
  170. // Read and Write functions
  171. // ========================
  172. /*
  173. ReadSingleByte reads a byte from a Record.
  174. */
  175. func (r *Record) ReadSingleByte(pos int) byte {
  176. return r.data[pos]
  177. }
  178. /*
  179. WriteSingleByte writes a byte to a Record.
  180. */
  181. func (r *Record) WriteSingleByte(pos int, value byte) {
  182. r.data[pos] = value
  183. r.SetDirty()
  184. }
  185. /*
  186. ReadUInt16 reads a 16-bit unsigned integer from a Record.
  187. */
  188. func (r *Record) ReadUInt16(pos int) uint16 {
  189. return (uint16(r.data[pos+0]) << 8) |
  190. (uint16(r.data[pos+1]) << 0)
  191. }
  192. /*
  193. WriteUInt16 writes a 16-bit unsigned integer to a Record.
  194. */
  195. func (r *Record) WriteUInt16(pos int, value uint16) {
  196. r.data[pos+0] = byte(value >> 8)
  197. r.data[pos+1] = byte(value >> 0)
  198. r.SetDirty()
  199. }
  200. /*
  201. ReadInt16 reads a 16-bit signed integer from a Record.
  202. */
  203. func (r *Record) ReadInt16(pos int) int16 {
  204. return (int16(r.data[pos+0]) << 8) |
  205. (int16(r.data[pos+1]) << 0)
  206. }
  207. /*
  208. WriteInt16 writes a 16-bit signed integer to a Record.
  209. */
  210. func (r *Record) WriteInt16(pos int, value int16) {
  211. r.data[pos+0] = byte(value >> 8)
  212. r.data[pos+1] = byte(value >> 0)
  213. r.SetDirty()
  214. }
  215. /*
  216. ReadUInt32 reads a 32-bit unsigned integer from a Record.
  217. */
  218. func (r *Record) ReadUInt32(pos int) uint32 {
  219. return (uint32(r.data[pos+0]) << 24) |
  220. (uint32(r.data[pos+1]) << 16) |
  221. (uint32(r.data[pos+2]) << 8) |
  222. (uint32(r.data[pos+3]) << 0)
  223. }
  224. /*
  225. WriteUInt32 writes a 32-bit unsigned integer to a Record.
  226. */
  227. func (r *Record) WriteUInt32(pos int, value uint32) {
  228. r.data[pos+0] = byte(value >> 24)
  229. r.data[pos+1] = byte(value >> 16)
  230. r.data[pos+2] = byte(value >> 8)
  231. r.data[pos+3] = byte(value >> 0)
  232. r.SetDirty()
  233. }
  234. /*
  235. ReadInt32 reads a 32-bit signed integer from a Record.
  236. */
  237. func (r *Record) ReadInt32(pos int) int32 {
  238. return (int32(r.data[pos+0]) << 24) |
  239. (int32(r.data[pos+1]) << 16) |
  240. (int32(r.data[pos+2]) << 8) |
  241. (int32(r.data[pos+3]) << 0)
  242. }
  243. /*
  244. WriteInt32 writes a 32-bit signed integer to a Record.
  245. */
  246. func (r *Record) WriteInt32(pos int, value int32) {
  247. r.data[pos+0] = byte(value >> 24)
  248. r.data[pos+1] = byte(value >> 16)
  249. r.data[pos+2] = byte(value >> 8)
  250. r.data[pos+3] = byte(value >> 0)
  251. r.SetDirty()
  252. }
  253. /*
  254. ReadUInt64 reads a 64-bit unsigned integer from a Record.
  255. */
  256. func (r *Record) ReadUInt64(pos int) uint64 {
  257. return (uint64(r.data[pos+0]) << 56) |
  258. (uint64(r.data[pos+1]) << 48) |
  259. (uint64(r.data[pos+2]) << 40) |
  260. (uint64(r.data[pos+3]) << 32) |
  261. (uint64(r.data[pos+4]) << 24) |
  262. (uint64(r.data[pos+5]) << 16) |
  263. (uint64(r.data[pos+6]) << 8) |
  264. (uint64(r.data[pos+7]) << 0)
  265. }
  266. /*
  267. WriteUInt64 writes a 64-bit unsigned integer to a Record.
  268. */
  269. func (r *Record) WriteUInt64(pos int, value uint64) {
  270. r.data[pos+0] = byte(value >> 56)
  271. r.data[pos+1] = byte(value >> 48)
  272. r.data[pos+2] = byte(value >> 40)
  273. r.data[pos+3] = byte(value >> 32)
  274. r.data[pos+4] = byte(value >> 24)
  275. r.data[pos+5] = byte(value >> 16)
  276. r.data[pos+6] = byte(value >> 8)
  277. r.data[pos+7] = byte(value >> 0)
  278. r.SetDirty()
  279. }
  280. /*
  281. MarshalBinary returns a binary representation of a Record.
  282. */
  283. func (r *Record) MarshalBinary() (data []byte, err error) {
  284. buf := new(bytes.Buffer)
  285. // Using a normal memory buffer this should always succeed
  286. r.WriteRecord(buf)
  287. return buf.Bytes(), nil
  288. }
  289. /*
  290. WriteRecord writes a record to an io.Writer.
  291. */
  292. func (r *Record) WriteRecord(iow io.Writer) error {
  293. if err := binary.Write(iow, binary.LittleEndian, r.id); err != nil {
  294. return err
  295. }
  296. if r.dirty {
  297. if err := binary.Write(iow, binary.LittleEndian, int8(1)); err != nil {
  298. return err
  299. }
  300. } else {
  301. if err := binary.Write(iow, binary.LittleEndian, int8(0)); err != nil {
  302. return err
  303. }
  304. }
  305. if err := binary.Write(iow, binary.LittleEndian, int64(r.transCount)); err != nil {
  306. return err
  307. }
  308. if err := binary.Write(iow, binary.LittleEndian, int64(len(r.data))); err != nil {
  309. return err
  310. }
  311. if _, err := iow.Write(r.data); err != nil {
  312. return err
  313. }
  314. // PageView is not persisted since it is derived from the record data
  315. return nil
  316. }
  317. /*
  318. UnmarshalBinary decodes a record from a binary blob.
  319. */
  320. func (r *Record) UnmarshalBinary(data []byte) error {
  321. buf := new(bytes.Buffer)
  322. buf.Write(data)
  323. return r.ReadRecord(buf)
  324. }
  325. /*
  326. ReadRecord decodes a record by reading from an io.Reader.
  327. */
  328. func (r *Record) ReadRecord(ior io.Reader) error {
  329. if err := binary.Read(ior, binary.LittleEndian, &r.id); err != nil {
  330. return err
  331. }
  332. r.pageView = nil
  333. var d int8
  334. if err := binary.Read(ior, binary.LittleEndian, &d); err == io.EOF {
  335. return io.ErrUnexpectedEOF
  336. }
  337. r.dirty = d == 1
  338. var t int64
  339. if err := binary.Read(ior, binary.LittleEndian, &t); err != nil {
  340. if err == io.EOF {
  341. return io.ErrUnexpectedEOF
  342. }
  343. return err
  344. }
  345. r.transCount = int(t)
  346. if err := binary.Read(ior, binary.LittleEndian, &t); err != nil {
  347. if err == io.EOF {
  348. return io.ErrUnexpectedEOF
  349. }
  350. return err
  351. }
  352. r.data = make([]byte, t)
  353. i, err := io.ReadFull(ior, r.data)
  354. if int64(i) != t {
  355. return io.ErrUnexpectedEOF
  356. }
  357. return err
  358. }
  359. /*
  360. ReadRecord decodes a record by reading from an io.Reader.
  361. */
  362. func ReadRecord(ior io.Reader) (*Record, error) {
  363. r := NewRecord(0, nil)
  364. if err := r.ReadRecord(ior); err != nil {
  365. return nil, err
  366. }
  367. return r, nil
  368. }