diff --git a/include/notcurses.h b/include/notcurses.h index e85bf8e54..9f7e05ed1 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -394,6 +394,11 @@ cell_double_wide_p(const cell* c){ return (c->channels & CELL_WIDEASIAN_MASK); } +static inline uint32_t +cell_egc_idx(const cell* c){ + return c->gcluster - 0x80; +} + // load up six cells with the EGCs necessary to draw a light, rounded box. // returns 0 on success, -1 on error. on error, any cells this function might // have loaded before the error are cell_release()d. diff --git a/src/bin/widecolor.c b/src/bin/widecolor.c index 355b46134..9b80ebc66 100644 --- a/src/bin/widecolor.c +++ b/src/bin/widecolor.c @@ -45,6 +45,7 @@ int widecolor_demo(struct notcurses* nc){ "Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα", "Vitrum edere possum; mihi non nocet", "🚬🌿💉💊☢☣🔫💣⚔🤜🤛🧠🦹🤺🏋️,🦔🐧🐣🦆🦢🦜🦉🐊🐸🦕 🦖🐬🐙🦂🦠🦀", + */ "Je puis mangier del voirre. Ne me nuit", "Je peux manger du verre, ça ne me fait pas mal", "Pòdi manjar de veire, me nafrariá pas", @@ -76,7 +77,7 @@ int widecolor_demo(struct notcurses* nc){ "Foddym gee glonney agh cha jean eh gortaghey mee", "᚛᚛ᚉᚑᚅᚔᚉᚉᚔᚋ ᚔᚈᚔ ᚍᚂᚐᚅᚑ ᚅᚔᚋᚌᚓᚅᚐ", "Con·iccim ithi nglano. Ním·géna", - "🗽🏴☭࿗☮࿘☭🏴🗽", + "☭࿗☮࿘☭", "Is féidir liom gloinne a ithe. Ní dhéanann sí dochar ar bith dom", "Ithim-sa gloine agus ní miste damh é", "S urrainn dhomh gloinne ithe; cha ghoirtich i mi", @@ -98,6 +99,7 @@ int widecolor_demo(struct notcurses* nc){ "Jag kan äta glas utan att skada mig", "Jeg kan spise glas, det gør ikke ondt på mig", "Æ ka æe glass uhen at det go mæ naue", + /* "က္ယ္ဝန္တော္၊က္ယ္ဝန္မ မ္ယက္စားနုိင္သည္။ ၎က္ရောင္ ထိခုိက္မ္ဟု မရ္ဟိပာ။", "ကျွန်တော် ကျွန်မ မှန်စားနိုင်တယ်။ ၎င်းကြောင့် ထိခိုက်မှုမရှိပါ။ ", "Tôi có thể ăn thủy tinh mà không hại gì", @@ -118,6 +120,7 @@ int widecolor_demo(struct notcurses* nc){ "Hiki iaʻu ke ʻai i ke aniani; ʻaʻole nō lā au e ʻeha", "E koʻana e kai i te karahi, mea ʻā, ʻaʻe hauhau", "ᐊᓕᒍᖅ ᓂᕆᔭᕌᖓᒃᑯ ᓱᕋᙱᑦᑐᓐᓇᖅᑐ", + */ "Naika məkmək kakshət labutay, pi weyk ukuk munk-sik nay", "Tsésǫʼ yishą́ągo bííníshghah dóó doo shił neezgai da", "mi kakne le nu citka le blaci .iku'i le se go'i na xrani m", @@ -132,6 +135,7 @@ int widecolor_demo(struct notcurses* nc){ "Isch kann Jlaas kimmeln, uuhne datt mich datt weh dääd", "Ich koann Gloos assn und doas dudd merr ni wii", "Мен шиша ейишим мумкин, аммо у менга зарар келтирмайди", + /* "আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না", "मी काच खाऊ शकतो, मला ते दुखत नाही", "ನನಗೆ ಹಾನಿ ಆಗದೆ, ನಾನು ಗಜನ್ನು ತಿನಬಹು", diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index fad6b0855..a4bcbf6ba 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -616,14 +616,9 @@ simple_cell_p(const cell* c){ return c->gcluster < 0x80; } -static inline uint32_t -extended_gcluster_idx(const cell* c){ - return c->gcluster - 0x80; -} - static inline const char* extended_gcluster(const ncplane* n, const cell* c){ - uint32_t idx = extended_gcluster_idx(c); + uint32_t idx = cell_egc_idx(c); return n->pool.pool + idx; } @@ -796,7 +791,7 @@ int ncplane_getc(const ncplane* n, cell* c, char** gclust){ void cell_release(ncplane* n, cell* c){ if(!simple_cell_p(c)){ - egcpool_release(&n->pool, extended_gcluster_idx(c)); + egcpool_release(&n->pool, cell_egc_idx(c)); c->gcluster = 0; // don't subject ourselves to double-release problems } } diff --git a/tests/egcpool.cpp b/tests/egcpool.cpp index 934c417b5..a166a5b14 100644 --- a/tests/egcpool.cpp +++ b/tests/egcpool.cpp @@ -113,3 +113,92 @@ TEST_F(EGCPoolTest, AddTwiceRemoveSecond) { EXPECT_EQ(u2 + 1, pool_.poolused); EXPECT_LT(0, pool_.poolwrite); } + +// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active. +// add EGCs to it past this boundary, and verify that they're all still +// accurate. +TEST_F(EGCPoolTest, ForceReallocation) { + std::array candidates; // offsets + char* firstalloc = nullptr; + for(auto i = 0u ; i < candidates.max_size() ; ++i){ + char mb[MB_CUR_MAX + 1]; + wchar_t wcs = i + 0x80; + auto r = wctomb(mb, wcs); + if(r < 0){ + candidates[i] = -1; + continue; + } + ASSERT_GE(sizeof(mb), r); + mb[r] = '\0'; + candidates[i] = egcpool_stash(&pool_, mb, r); + ASSERT_GT(1u << 24u, candidates[i]); + if(!firstalloc){ + firstalloc = pool_.pool; + } + } + // verify that we moved the pool at least once + ASSERT_NE(pool_.pool, firstalloc); + for(auto i = 0u ; i < candidates.max_size() ; ++i){ + auto stored = pool_.pool + candidates[i]; + char mb[MB_CUR_MAX + 1]; + wchar_t wcs = i + 0x80; + auto r = wctomb(mb, wcs); + if(r < 0){ + ASSERT_EQ(-1, candidates[i]); + continue; + } + ASSERT_LT(0, r); + mb[r] = '\0'; + EXPECT_STREQ(mb, stored); + } +} + +// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active. +// add EGCs to it past this boundary, and verify that they're all still +// accurate. +TEST_F(EGCPoolTest, ForceReallocationWithRemovals) { + std::array candidates; // offsets + char* curpool = nullptr; + for(auto i = 0u ; i < candidates.max_size() ; ++i){ + char mb[MB_CUR_MAX + 1]; + wchar_t wcs = i + 0x80; + auto r = wctomb(mb, wcs); + if(r < 0){ + candidates[i] = -1; + continue; + } + ASSERT_GE(sizeof(mb), r); + mb[r] = '\0'; + candidates[i] = egcpool_stash(&pool_, mb, r); + ASSERT_GT(1u << 24u, candidates[i]); + if(pool_.pool != curpool){ + // cut through and release a bunch of them + if(curpool){ + for(auto j = 0u ; j < i ; j += 3){ + if(candidates[j] >= 0){ + egcpool_release(&pool_, candidates[j]); + candidates[j] = -1; + } + } + } + curpool = pool_.pool; + } + } + for(auto i = 0u ; i < candidates.max_size() ; ++i){ + auto stored = pool_.pool + candidates[i]; + char mb[MB_CUR_MAX + 1]; + wchar_t wcs = i + 0x80; + auto r = wctomb(mb, wcs); + if(r < 0){ + ASSERT_EQ(-1, candidates[i]); + continue; + } + ASSERT_LT(0, r); + mb[r] = '\0'; + if(i % 3 == 0){ + EXPECT_STREQ(mb, ""); + }else{ + EXPECT_STREQ(mb, stored); + } + } +}