Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Very initial version of zid migration |
|---|---|
| Timelines: | family | ancestors | descendants | both | b36 |
| Files: | files | file ages | folders |
| SHA3-256: |
2b742a0dc8ede22eba575f3ea2df15d7 |
| User & Date: | stern 2024-06-21 17:39:20.226 |
Context
|
2024-06-21
| ||
| 17:47 | Add a check for already given zids ... (check-in: 8475ed0c2d user: stern tags: b36) | |
| 17:39 | Very initial version of zid migration ... (check-in: 2b742a0dc8 user: stern tags: b36) | |
| 15:51 | Rename ZidO constant to InvalidO (an use the correct branch name b36) ... (check-in: 4cc1d16330 user: stern tags: b36) | |
Changes
Changes to zettel/id/id.go.
| ︙ | ︙ | |||
161 162 163 164 165 166 167 |
}
res, err := ParseO(s)
if err != nil {
panic(err)
}
return res
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
}
res, err := ParseO(s)
if err != nil {
panic(err)
}
return res
}
// ----- Base36 Zids
// Zid is the internal identifier of a zettel. Typically, it is a number in
// the range 1..36^4-1 (1..1679615). As an external representation, this
// number is encode by four digits / lowercase letters.
type Zid uint32
// Some important ZettelIDs.
const (
Invalid = Zid(0) // Invalid is a Zid that will never be valid
)
const maxZid = 36*36*36*36 - 1
// ParseUint interprets a string as a possible zettel identifier
// and returns its integer value.
func ParseUint(s string) (uint64, error) {
res, err := strconv.ParseUint(s, 36, 21)
if err != nil {
return 0, err
}
if res == 0 || res > maxZid {
return res, strconv.ErrRange
}
return res, nil
}
// Parse interprets a string as a zettel identification and
// returns its value.
func Parse(s string) (Zid, error) {
if len(s) != 4 {
return Invalid, strconv.ErrSyntax
}
res, err := ParseUint(s)
if err != nil {
return Invalid, err
}
return Zid(res), nil
}
// MustParse tries to interpret a string as a zettel identifier and returns
// its value or panics otherwise.
func MustParse(s api.ZettelID) Zid {
zid, err := Parse(string(s))
if err == nil {
return zid
}
panic(err)
}
// String converts the zettel identification to a string of 14 digits.
// Only defined for valid ids.
func (zid Zid) String() string {
var result [4]byte
zid.toByteArray(&result)
return string(result[:])
}
// ZettelID return the zettel identification as a api.ZettelID.
func (zid Zid) ZettelID() api.ZettelID { return api.ZettelID(zid.String()) }
// Bytes converts the zettel identification to a byte slice of 14 digits.
// Only defined for valid ids.
func (zid Zid) Bytes() []byte {
var result [4]byte
zid.toByteArray(&result)
return result[:]
}
// toByteArray converts the Zid into a fixed byte array, usable for printing.
//
// Based on idea by Daniel Lemire: "Converting integers to fix-digit representations quickly"
// https://lemire.me/blog/2021/11/18/converting-integers-to-fix-digit-representations-quickly/
func (zid Zid) toByteArray(result *[4]byte) {
d12 := uint32(zid) / (36 * 36)
d1 := d12 / 36
d2 := d12 % 36
d34 := uint32(zid) % (36 * 36)
d3 := d34 / 36
d4 := d34 % 36
const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
result[0] = digits[d1]
result[1] = digits[d2]
result[2] = digits[d3]
result[3] = digits[d4]
}
// IsValid determines if zettel id is a valid one, e.g. consists of max. 14 digits.
func (zid Zid) IsValid() bool { return 0 < zid && zid <= maxZid }
|
Changes to zettel/id/id_test.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package id_test provides unit tests for testing zettel id specific functions.
package id_test
import (
"testing"
"zettelstore.de/z/zettel/id"
)
func TestIsValidO(t *testing.T) {
t.Parallel()
| > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package id_test provides unit tests for testing zettel id specific functions.
package id_test
import (
"strings"
"testing"
"zettelstore.de/z/zettel/id"
)
func TestIsValidO(t *testing.T) {
t.Parallel()
|
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
func BenchmarkBytesO(b *testing.B) {
var bs []byte
for range b.N {
bs = id.ZidO(12345678901200).Bytes()
}
bResult = bs
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
func BenchmarkBytesO(b *testing.B) {
var bs []byte
for range b.N {
bs = id.ZidO(12345678901200).Bytes()
}
bResult = bs
}
// ----- Base36
func TestIsValid(t *testing.T) {
t.Parallel()
validIDs := []string{
"0001", "0020", "0300", "4000",
"zzzz", "ZZZZ", "Cafe", "bAbE",
}
for i, sid := range validIDs {
zid, err := id.Parse(sid)
if err != nil {
t.Errorf("i=%d: sid=%q is not valid, but should be. err=%v", i, sid, err)
}
if s := zid.String(); !strings.EqualFold(s, sid) {
t.Errorf("i=%d: zid=%v does not format to %q, but to %q", i, zid, sid, s)
}
}
invalidIDs := []string{
"", "0", "a", "de", "dfg", "abcde",
"012.",
"+1234", "+123",
}
for i, sid := range invalidIDs {
if zid, err := id.Parse(sid); err == nil {
t.Errorf("i=%d: sid=%q is valid (zid=%s), but should not be", i, sid, zid)
}
}
}
|
Added zettel/id/migrate.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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
//-----------------------------------------------------------------------------
// Copyright (c) 2024-present Detlef Stern
//
// This file is part of Zettelstore.
//
// Zettelstore is licensed under the latest version of the EUPL (European Union
// Public License). Please see file LICENSE.txt for your rights and obligations
// under this license.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------
package id
import (
"fmt"
"maps"
)
// This is for migration of Zid0 to Zid.
// ZidMigrator does the actual migration.
type ZidMigrator struct {
defined, workset map[ZidO]Zid
lastZidO ZidO
nextZid Zid
ranges []zidRange
}
type zidRange struct {
lowO, highO ZidO
base Zid
}
// NewZidMigrator creates a new zid migrator.
func NewZidMigrator() *ZidMigrator {
defined := map[ZidO]Zid{
0: 0, // Invalid
1: MustParse("0001"), // Zettelstore Version
2: MustParse("0002"), // Zettelstore Host
3: MustParse("0003"), // Zettelstore Operating System
4: MustParse("0004"), // Zettelstore License
5: MustParse("0005"), // Zettelstore Contributors
6: MustParse("0006"), // Zettelstore Dependencies
7: MustParse("0007"), // Zettelstore Log
8: MustParse("0008"), // Zettelstore Memory
20: MustParse("000g"), // Zettelstore Box Manager
90: MustParse("000t"), // Zettelstore Supported Metadata Keys
92: MustParse("000v"), // Zettelstore Supported Parser
96: MustParse("000x"), // Zettelstore Startup Configuration
100: MustParse("000z"), // Zettelstore Runtime Configuration
10100: MustParse("0010"), // Zettelstore Base HTML Template
10200: MustParse("0011"), // Zettelstore Login Form HTML Template
10300: MustParse("0012"), // Zettelstore List Zettel HTML Template
10401: MustParse("0013"), // Zettelstore Detail HTML Template
10402: MustParse("0014"), // Zettelstore Info HTML Template
10403: MustParse("0015"), // Zettelstore Form HTML Template
10404: MustParse("0016"), // Zettelstore Rename Form HTML Template
10405: MustParse("0017"), // Zettelstore Delete HTML Template
10700: MustParse("0018"), // Zettelstore Error HTML Template
19000: MustParse("0021"), // Zettelstore Sxn Start Code
19990: MustParse("0022"), // Zettelstore Sxn Base Code
20001: MustParse("0030"), // Zettelstore Base CSS
25001: MustParse("0031"), // Zettelstore User CSS
40001: MustParse("0032"), // Generic Emoji
59900: MustParse("0020"), // Zettelstore Sxn Prelude
60010: MustParse("0041"), // zettel
60020: MustParse("0042"), // confguration
60030: MustParse("0043"), // role
60040: MustParse("0044"), // tag
90000: MustParse("0050"), // New Menu
90001: MustParse("0051"), // New Zettel
90002: MustParse("0052"), // New User
90003: MustParse("0053"), // New Tag
90004: MustParse("0054"), // New Role
100000000: MustParse("0100"), // Zettelstore Manual (bis 02zz)
200000000: MustParse("0300"), // Reserviert (bis 0tzz)
9000000000: MustParse("0u00"), // Externe Anwendungen (bis 0zzz)
DefaultHomeZidO: MustParse("1000"), // Default home zettel
}
return &ZidMigrator{
defined: defined,
workset: maps.Clone(defined),
lastZidO: InvalidO,
nextZid: MustParse("1001"),
ranges: []zidRange{
{10000, 19999, MustParse("0010")},
{20000, 29999, MustParse("0030")},
{40000, 49999, MustParse("0032")},
{50000, 59999, MustParse("0020")},
{60000, 69999, MustParse("0040")},
{90000, 99999, MustParse("0050")},
},
}
}
// Migrate an old Zid to a new one.
//
// Old zids must increase.
func (zm *ZidMigrator) Migrate(zidO ZidO) (Zid, error) {
if zid, found := zm.workset[zidO]; found {
return zid, nil
}
if zidO <= zm.lastZidO {
return Invalid, fmt.Errorf("out of sequence: %v", zidO)
}
zm.lastZidO = zidO
if (zidO < 10000) ||
(30000 <= zidO && zidO < 40000) ||
(70000 <= zidO && zidO < 90000) ||
(100000 <= zidO && zidO < 100000000) ||
(200000000 <= zidO && zidO < 9000001000) ||
(9000002000 <= zidO && zidO < DefaultHomeZidO) {
return 0, fmt.Errorf("old Zid out of supported range: %v", zidO)
}
if DefaultHomeZidO < zidO {
zid := zm.nextZid
zm.nextZid++
zm.workset[zidO] = zid
return zid, nil
}
for _, zr := range zm.ranges {
if zidO < zr.lowO || zr.highO < zidO {
continue
}
zid := zm.retrieveNextInRange(zr.lowO, zr.highO)
zm.workset[zidO] = zid
return zid, nil
}
return Invalid, nil
}
func (zm *ZidMigrator) retrieveNextInRange(lowO, highO ZidO) Zid {
var currentMax Zid
for zidO, zid := range zm.workset {
if lowO <= zidO && zidO <= highO && currentMax < zid {
currentMax = zid
}
}
return currentMax + 1
}
|
Added zettel/id/migrate_test.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2024-present Detlef Stern
//
// This file is part of Zettelstore.
//
// Zettelstore is licensed under the latest version of the EUPL (European Union
// Public License). Please see file LICENSE.txt for your rights and obligations
// under this license.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------
package id_test
import (
"testing"
"zettelstore.de/z/zettel/id"
)
func TestMigrat(t *testing.T) {
testcases := []struct {
inp []id.ZidO
err string
exp []id.Zid
}{
{[]id.ZidO{1, 2, 3}, "", []id.Zid{1, 2, 3}},
{[]id.ZidO{3, 2, 1}, "", []id.Zid{3, 2, 1}},
{[]id.ZidO{20240224123456, 19700101000000}, "out of sequence: 19700101000000", []id.Zid{46657, 1}},
}
for i, tc := range testcases {
migrator := id.NewZidMigrator()
for pos, zidO := range tc.inp {
zid, err := migrator.Migrate(zidO)
if err != nil {
if tc.err == "" {
t.Errorf("%d: no error expected, but got: %v", i, err)
} else if err.Error() != tc.err {
t.Errorf("%d: error %v expected, but got %v", i, tc.err, err)
}
continue
}
if exp := tc.exp[pos]; exp != zid {
t.Errorf("%d: %v should migrate to %v, but got: %v", i, zidO, exp, zid)
}
}
}
}
|