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 |
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) } } } } |