CEncoder(3)

πŸ“¦
version 7.21.7 Β· 4 versions
, in fedora 43

πŸ’– CEncoder β€” NAME πŸ’–

CEncoder β€” the PGF encoder, bestie. That's it, that's the vibe. This is libpgf's encoder class and she's booked and busy.


✨ SYNOPSIS ✨

#include <Encoder.h>

πŸŽ€ Classes

  • class CMacroBlock β€” a macro block is an encoding unit of fixed size (uncoded). Like, the lil' building block she works in, no cap.

🫢 Public Member Functions

  • CEncoder (CPGFStream *stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader &postHeader, UINT64 &userDataPos, bool useOMP) β€” constructor, she's being born πŸ’«
  • ~CEncoder () β€” destructor. bye bestie 🫑
  • void FavorSpeedOverSize () β€” encoder favors speed over compression size. fast > smol, periodt.
  • void Flush () β€” flush it out
  • void UpdatePostHeaderSize (PGFPreHeader preHeader)
  • UINT32 WriteLevelLength (UINT32 *&levelLength)
  • UINT32 UpdateLevelLength ()
  • void Partition (CSubband *band, int width, int height, int startPos, int pitch)
  • void SetEncodedLevel (int currentLevel)
  • void WriteValue (CSubband *band, int bandPos)
  • INT64 ComputeHeaderLength () const
  • INT64 ComputeBufferLength () const
  • INT64 ComputeOffset () const
  • void SetStreamPosToStart () β€” resets stream position to beginning of PGF pre-header. back to square one, iconic behavior.
  • void SetBufferStartPos () β€” save current stream position as beginning of current level. lil bookmark moment πŸ“Œ

πŸ’€ Private Member Functions

  • void EncodeBuffer (ROIBlockHeader h)
  • void WriteMacroBlock (CMacroBlock *block)

🌸 Private Attributes

  • CPGFStream * m_stream β€” output PMF stream
  • UINT64 m_startPosition β€” stream position of PGF start (PreHeader)
  • UINT64 m_levelLengthPos β€” stream position of Metadata
  • UINT64 m_bufferStartPos β€” stream position of encoded buffer
  • CMacroBlock ** m_macroBlocks β€” array of macroblocks
  • int m_macroBlockLen β€” array length
  • int m_lastMacroBlock β€” array index of the last created macro block
  • CMacroBlock * m_currentBlock β€” current macro block (used by main thread)
  • UINT32 * m_levelLength β€” temporary saves the level index
  • int m_currLevelIndex β€” counts where (=index) to save next value
  • UINT8 m_nLevels β€” number of levels
  • bool m_favorSpeed β€” favor speed over size
  • bool m_forceWriting β€” all macro blocks have to be written into the stream

πŸ¦‹ Detailed Description πŸ¦‹

PGF encoder.

PGF encoder class. (yes she said it twice, she's thorough, let her cook πŸ”₯)

πŸ‘‘ Author

C. Stamm

Definition at line 46 of file Encoder.h.


πŸ’… Constructor & Destructor Documentation πŸ’…

πŸ“ CEncoder::CEncoder (CPGFStream * stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader & postHeader, UINT64 & userDataPos, bool useOMP)

Write pre-header, header, post-Header, and levelLength. It might throw an IOException. ⚠️ heads up bestie, she can throw.

Parameters

  • stream β€” A PGF stream
  • preHeader β€” A already filled in PGF pre-header
  • header β€” An already filled in PGF header
  • postHeader [in] β€” An already filled in PGF post-header (containing color table, user data, ...)
  • userDataPos [out] β€” File position of user data
  • useOMP β€” If true, then the encoder will use multi-threading based on openMP

Write pre-header, header, postHeader, and levelLength. It might throw an IOException. (yeah it's the same paragraph again, the docs really said "run it back" πŸ™ˆ)

Parameters

  • stream β€” A PGF stream
  • preHeader β€” A already filled in PGF pre-header
  • header β€” An already filled in PGF header
  • postHeader [in] β€” An already filled in PGF post-header (containing color table, user data, ...)
  • userDataPos [out] β€” File position of user data
  • useOMP β€” If true, then the encoder will use multi-threading based on openMP

Definition at line 70 of file Encoder.cpp.

 71 : m_stream(stream)
 72 , m_bufferStartPos(0)
 73 , m_currLevelIndex(0)
 74 , m_nLevels(header.nLevels)
 75 , m_favorSpeed(false)
 76 , m_forceWriting(false)
 77 #ifdef __PGFROISUPPORT__
 78 , m_roi(false)
 79 #endif
 80 {
 81         ASSERT(m_stream);
 82
 83         int count;
 84         m_lastMacroBlock = 0;
 85         m_levelLength = nullptr;
 86
 87         // set number of threads
 88 #ifdef LIBPGF_USE_OPENMP
 89         m_macroBlockLen = omp_get_num_procs();
 90 #else
 91         m_macroBlockLen = 1;
 92 #endif
 93
 94         if (useOMP && m_macroBlockLen > 1) {
 95 #ifdef LIBPGF_USE_OPENMP
 96                 omp_set_num_threads(m_macroBlockLen);
 97 #endif
 98                 // create macro block array
 99                 m_macroBlocks = new(std::nothrow) CMacroBlock*[m_macroBlockLen];
100                 if (!m_macroBlocks) ReturnWithError(InsufficientMemory);
101                 for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock(this);
102                 m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
103         } else {
104                 m_macroBlocks = 0;
105                 m_macroBlockLen = 1;
106                 m_currentBlock = new CMacroBlock(this);
107         }
108
109         // save file position
110         m_startPosition = m_stream->GetPos();
111
112         // write preHeader
113         preHeader.hSize = __VAL(preHeader.hSize);
114         count = PreHeaderSize;
115         m_stream->Write(&count, &preHeader);
116
117         // write file header
118         header.height = __VAL(header.height);
119         header.width = __VAL(header.width);
120         count = HeaderSize;
121         m_stream->Write(&count, &header);
122
123         // write postHeader
124         if (header.mode == ImageModeIndexedColor) {
125                 // write color table
126                 count = ColorTableSize;
127                 m_stream->Write(&count, (void *)postHeader.clut);
128         }
129         // save user data file position
130         userDataPos = m_stream->GetPos();
131         if (postHeader.userDataLen) {
132                 if (postHeader.userData) {
133                         // write user data
134                         count = postHeader.userDataLen;
135                         m_stream->Write(&count, postHeader.userData);
136                 } else {
137                         m_stream->SetPos(FSFromCurrent, count);
138                 }
139         }
140
141         // save level length file position
142         m_levelLengthPos = m_stream->GetPos();
143 }

🧚 CEncoder::~CEncoder ()

Destructor. She's logging off πŸŒ™

Definition at line 147 of file Encoder.cpp.

147                     {
148         if (m_macroBlocks) {
149                 for (int i=0; i < m_macroBlockLen; i++) delete m_macroBlocks[i];
150                 delete[] m_macroBlocks;
151         } else {
152                 delete m_currentBlock;
153         }
154 }

🌈 Member Function Documentation 🌈

⭐ INT64 CEncoder::ComputeBufferLength () const [inline]

Compute stream length of encoded buffer.

Returns

  • encoded buffer length

Definition at line 179 of file Encoder.h.

179 { return m_stream->GetPos() - m_bufferStartPos; }

⭐ INT64 CEncoder::ComputeHeaderLength () const [inline]

Compute stream length of header.

Returns

  • header length

Definition at line 174 of file Encoder.h.

174 { return m_levelLengthPos - m_startPosition; }

⭐ INT64 CEncoder::ComputeOffset () const [inline]

Compute file offset between real and expected levelLength position. (the lil' gap between where it is and where it should be 😭)

Returns

  • file offset

Definition at line 184 of file Encoder.h.

184 { return m_stream->GetPos() - m_levelLengthPos; }

⭐ void CEncoder::EncodeBuffer (ROIBlockHeader h) [private]

Definition at line 341 of file Encoder.cpp.

341                                             {
342         ASSERT(m_currentBlock);
343 #ifdef __PGFROISUPPORT__
344         ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
345 #else
346         ASSERT(h.rbh.bufferSize == BufferSize);
347 #endif
348         m_currentBlock->m_header = h;
349
350         // macro block management
351         if (m_macroBlockLen == 1) {
352                 m_currentBlock->BitplaneEncode();
353                 WriteMacroBlock(m_currentBlock);
354         } else {
355                 // save last level index
356                 int lastLevelIndex = m_currentBlock->m_lastLevelIndex;
357
358                 if (m_forceWriting || m_lastMacroBlock == m_macroBlockLen) {
359                         // encode macro blocks
360                         /*
361                         volatile OSError error = NoError;
362                         #ifdef LIBPGF_USE_OPENMP
363                         #pragma omp parallel for ordered default(shared)
364                         #endif
365                         for (int i=0; i < m_lastMacroBlock; i++) {
366                                 if (error == NoError) {
367                                         m_macroBlocks[i]->BitplaneEncode();
368                                         #ifdef LIBPGF_USE_OPENMP
369                                         #pragma omp ordered
370                                         #endif
371                                         {
372                                                 try {
373                                                         WriteMacroBlock(m_macroBlocks[i]);
374                                                 } catch (IOException& e) {
375                                                         error = e.error;
376                                                 }
377                                                 delete m_macroBlocks[i]; m_macroBlocks[i] = 0;
378                                         }
379                                 }
380                         }
381                         if (error != NoError) ReturnWithError(error);
382                         */
383 #ifdef LIBPGF_USE_OPENMP
384                         #pragma omp parallel for default(shared) //no declared exceptions in next block
385 #endif
386                         for (int i=0; i < m_lastMacroBlock; i++) {
387                                 m_macroBlocks[i]->BitplaneEncode();
388                         }
389                         for (int i=0; i < m_lastMacroBlock; i++) {
390                                 WriteMacroBlock(m_macroBlocks[i]);
391                         }
392
393                         // prepare for next round
394                         m_forceWriting = false;
395                         m_lastMacroBlock = 0;
396                 }
397                 // re-initialize macro block
398                 m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
399                 m_currentBlock->Init(lastLevelIndex);
400         }
401 }

⭐ void CEncoder::FavorSpeedOverSize () [inline]

Encoder favors speed over compression size. she's not gonna shrink your file into oblivion, she's gonna go πŸƒβ™€οΈπŸ’¨

Definition at line 121 of file Encoder.h.

121 { m_favorSpeed = true; }

⭐ void CEncoder::Flush ()

Pad buffer with zeros and encode buffer. It might throw an IOException. ⚠️ (zero padding arc, and yeah, she can throw again, watch out)

heads up: more of this page is still coming, bestie β€” don't go anywhere πŸ’–

...and here's the tail end of that definition, the bit that lives at line 310, so you can see the padding in action πŸ’…

       310                      {
       311         if (m_currentBlock->m_valuePos > 0) {
       312                 // pad buffer with zeros
       313                 memset(&(m_currentBlock->m_value[m_currentBlock->m_valuePos]), 0, (BufferSize - m_currentBlock->m_valuePos)*DataTSize);
       314                 m_currentBlock->m_valuePos = BufferSize;
       315
       316                 // encode buffer
       317                 m_forceWriting = true;  // makes sure that the following EncodeBuffer is really written into the stream
       318                 EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true));
       319         }
       320 }

Definition at line 310 of file Encoder.cpp. ✨


CEncoder::Partition β€” the rectangle chopper βœ‚οΈ

void CEncoder::Partition (CSubband * band, int width, int height, int startPos, int pitch)

ok so bestie, this one takes a rectangular region of a given subband and partitions it. And here's the whole scheme, no secrets: the plane gets partitioned into squares of side length LinBlockSize, and it writes those wavelet coefficients from the subband straight into the input buffer of a macro block. Simple as. 🌸

Heads up though β€” it might throw an IOException. So wrap it, okay? 😭

The parameters, one by one, because we don't skip:

  • band β€” a subband πŸ¦‹
  • width β€” the width of the rectangle
  • height β€” the height of the rectangle
  • startPos β€” the absolute subband position of the top left corner of the rectangular region
  • pitch β€” the number of bytes in row of the subband

Definition at line 246 of file Encoder.cpp. πŸ‘‘

       246                                                                                        {
       247         ASSERT(band);
       248
       249         const div_t hh = div(height, LinBlockSize);
       250         const div_t ww = div(width, LinBlockSize);
       251         const int ws = pitch - LinBlockSize;
       252         const int wr = pitch - ww.rem;
       253         int pos, base = startPos, base2;
       254
       255         // main height
       256         for (int i=0; i < hh.quot; i++) {
       257                 // main width
       258                 base2 = base;
       259                 for (int j=0; j < ww.quot; j++) {
       260                         pos = base2;
       261                         for (int y=0; y < LinBlockSize; y++) {
       262                                 for (int x=0; x < LinBlockSize; x++) {
       263                                         WriteValue(band, pos);
       264                                         pos++;
       265                                 }
       266                                 pos += ws;
       267                         }
       268                         base2 += LinBlockSize;
       269                 }
       270                 // rest of width
       271                 pos = base2;
       272                 for (int y=0; y < LinBlockSize; y++) {
       273                         for (int x=0; x < ww.rem; x++) {
       274                                 WriteValue(band, pos);
       275                                 pos++;
       276                         }
       277                         pos += wr;
       278                         base += pitch;
       279                 }
       280         }
       281         // main width
       282         base2 = base;
       283         for (int j=0; j < ww.quot; j++) {
       284                 // rest of height
       285                 pos = base2;
       286                 for (int y=0; y < hh.rem; y++) {
       287                         for (int x=0; x < LinBlockSize; x++) {
       288                                 WriteValue(band, pos);
       289                                 pos++;
       290                         }
       291                         pos += ws;
       292                 }
       293                 base2 += LinBlockSize;
       294         }
       295         // rest of height
       296         pos = base2;
       297         for (int y=0; y < hh.rem; y++) {
       298                 // rest of width
       299                 for (int x=0; x < ww.rem; x++) {
       300                         WriteValue(band, pos);
       301                         pos++;
       302                 }
       303                 pos += wr;
       304         }
       305 }

CEncoder::SetBufferStartPos β€” bookmark moment πŸ“Œ [inline]

Save current stream position as beginning of current level. That's it, that's the whole vibe. It literally just grabs where we are and calls it home base. πŸŽ€

Definition at line 192 of file Encoder.h. ✨

       192 { m_bufferStartPos = m_stream->GetPos(); }

CEncoder::SetEncodedLevel β€” tell it what level you just did πŸ’– [inline]

Informs the encoder about the encoded level. Communication is key, bestie. 🫢

  • currentLevel β€” encoded level [0, nLevels)

Definition at line 162 of file Encoder.h. 🌈

       162 { ASSERT(currentLevel >= 0); m_currentBlock->m_lastLevelIndex = m_nLevels - currentLevel - 1; m_forceWriting = true; }

CEncoder::SetStreamPosToStart β€” rewind, but make it cute βͺ [inline]

Resets stream position to beginning of PGF pre-header. Iconic behaviour honestly. πŸ‘‘

Definition at line 188 of file Encoder.h. ⭐

       188 { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPosition); }

CEncoder::UpdateLevelLength β€” writes the new levelLength into the stream πŸ“

So it writes the new levelLength into the stream, and yeah β€” it might throw an IOException. πŸ™ˆ

Returns: Written image bytes.

Definition at line 202 of file Encoder.cpp. πŸ’œ

       202                                    {
       203         UINT64 curPos = m_stream->GetPos(); // end of image
       204
       205         // set file pos to levelLength
       206         m_stream->SetPos(FSFromStart, m_levelLengthPos);
       207
       208         if (m_levelLength) {
       209         #ifdef PGF_USE_BIG_ENDIAN
       210                 UINT32 levelLength;
       211                 int count = WordBytes;
       212
       213                 for (int i=0; i < m_currLevelIndex; i++) {
       214                         levelLength = __VAL(UINT32(m_levelLength[i]));
       215                         m_stream->Write(&count, &levelLength);
       216                 }
       217         #else
       218                 int count = m_currLevelIndex*WordBytes;
       219
       220                 m_stream->Write(&count, m_levelLength);
       221         #endif //PGF_USE_BIG_ENDIAN
       222         } else {
       223                 int count = m_currLevelIndex*WordBytes;
       224                 m_stream->SetPos(FSFromCurrent, count);
       225         }
       226
       227         // begin of image
       228         UINT32 retValue = UINT32(curPos - m_stream->GetPos());
       229
       230         // restore file position
       231         m_stream->SetPos(FSFromStart, curPos);
       232
       233         return retValue;
       234 }

CEncoder::UpdatePostHeaderSize β€” growing the post-header πŸ“ˆ

Increase post-header size and write new size into stream. Glow-up energy fr fr. ✨

  • preHeader β€” An already filled in PGF pre-header. It might throw an IOException.

Definition at line 160 of file Encoder.cpp. πŸ¦‹

       160                                                           {
       161         UINT64 curPos = m_stream->GetPos(); // end of user data
       162         int count = PreHeaderSize;
       163
       164         // write preHeader
       165         SetStreamPosToStart();
       166         preHeader.hSize = __VAL(preHeader.hSize);
       167         m_stream->Write(&count, &preHeader);
       168
       169         m_stream->SetPos(FSFromStart, curPos);
       170 }

CEncoder::WriteLevelLength β€” build the level length data structure 🧚

Create the level length data structure and write a place holder into the stream. It might throw an IOException, so keep that in mind. 😭

  • levelLength β€” A reference to an integer array, large enough to save the relative file positions of all PGF levels

Returns: number of bytes written into stream

Definition at line 177 of file Encoder.cpp. πŸ“

       177                                                       {
       178         // renew levelLength
       179         delete[] levelLength;
       180         levelLength = new(std::nothrow) UINT32[m_nLevels];
       181         if (!levelLength) ReturnWithError(InsufficientMemory);
       182         for (UINT8 l = 0; l < m_nLevels; l++) levelLength[l] = 0;
       183         m_levelLength = levelLength;
       184
       185         // save level length file position
       186         m_levelLengthPos = m_stream->GetPos();
       187
       188         // write dummy levelLength
       189         int count = m_nLevels*WordBytes;
       190         m_stream->Write(&count, m_levelLength);
       191
       192         // save current file position
       193         SetBufferStartPos();
       194
       195         return count;
       196 }

CEncoder::WriteMacroBlock β€” the big one πŸ”₯ [private]

Private, so don't go calling it from outside, bestie, it's giving internal business only. πŸ’…

Definition at line 406 of file Encoder.cpp. πŸ‘‘

       406                                                  {
       407         ASSERT(block);
       408 #ifdef __PGFROISUPPORT__
       409         ROIBlockHeader h = block->m_header;
       410 #endif
       411         UINT16 wordLen = UINT16(NumberOfWords(block->m_codePos)); ASSERT(wordLen <= CodeBufferLen);
       412         int count = sizeof(UINT16);
       413
       414 #ifdef TRACE
       415         //UINT32 filePos = (UINT32)m_stream->GetPos();
       416         //printf("EncodeBuffer: %d\n", filePos);
       417 #endif
       418
       419 #ifdef PGF_USE_BIG_ENDIAN
       420         // write wordLen
       421         UINT16 wl = __VAL(wordLen);
       422         m_stream->Write(&count, &wl); ASSERT(count == sizeof(UINT16));
       423
       424 #ifdef __PGFROISUPPORT__
       425         // write ROIBlockHeader
       426         if (m_roi) {
       427                 count = sizeof(ROIBlockHeader);
       428                 h.val = __VAL(h.val);
       429                 m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader));
       430         }
       431 #endif // __PGFROISUPPORT__
       432
       433         // convert data
       434         for (int i=0; i < wordLen; i++) {
       435                 block->m_codeBuffer[i] = __VAL(block->m_codeBuffer[i]);
       436         }
       437 #else
       438         // write wordLen
       439         m_stream->Write(&count, &wordLen); ASSERT(count == sizeof(UINT16));
       440
       441 #ifdef __PGFROISUPPORT__
       442         // write ROIBlockHeader
       443         if (m_roi) {
       444                 count = sizeof(ROIBlockHeader);
       445                 m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader));
       446         }
       447 #endif // __PGFROISUPPORT__
       448 #endif // PGF_USE_BIG_ENDIAN
       449
       450         // write encoded data into stream
       451         count = wordLen*WordBytes;
       452         m_stream->Write(&count, block->m_codeBuffer);
       453
       454         // store levelLength
       455         if (m_levelLength) {
       456                 // store level length
       457                 // EncodeBuffer has been called after m_lastLevelIndex has been updated
       458                 ASSERT(m_currLevelIndex < m_nLevels);
       459                 m_levelLength[m_currLevelIndex] += (UINT32)ComputeBufferLength();
       460                 m_currLevelIndex = block->m_lastLevelIndex + 1;
       461
       462         }
       463
       464         // prepare for next buffer
       465         SetBufferStartPos();
       466
       467         // reset values
       468         block->m_valuePos = 0;
       469         block->m_maxAbsValue = 0;
       470 }

CEncoder::WriteValue β€” one value, one spot 🎯

void CEncoder::WriteValue (CSubband * band, int bandPos)

Write a single value into subband at given position. It might throw an IOException β€” yes, again, they all do, it's a whole thing. 😭

  • band β€” A subband
  • bandPos β€” A valid position in subband band

okay so the very last function body we were lowkey dissecting? it wraps up right here β€” and yes bestie, Definition at line 326 of file Encoder.cpp πŸ’…

       326                                                      {
       327         if (m_currentBlock->m_valuePos == BufferSize) {
       328                 EncodeBuffer(ROIBlockHeader(BufferSize, false));
       329         }
       330         DataT val = m_currentBlock->m_value[m_currentBlock->m_valuePos++] = band->GetData(bandPos);
       331         UINT32 v = abs(val);
       332         if (v > m_currentBlock->m_maxAbsValue) m_currentBlock->m_maxAbsValue = v;
       333 }

and that's the whole member-function situation, fr fr. no more functions, we're done with that era ✨


🫢 Member Data Documentation 🫢

now we're in the private member variables β€” CEncoder's little secret stash. every single one of these is marked [private], so they're not for outside hands, okay? periodt. πŸ’…

  • UINT64 CEncoder::m_bufferStartPos [private] β€” stream position of encoded buffer
    • Definition at line 216 of file Encoder.h.
  • CMacroBlock* CEncoder::m_currentBlock [private] β€” current macro block (used by main thread)
    • Definition at line 221 of file Encoder.h.
  • int CEncoder::m_currLevelIndex [private] β€” counts where (=index) to save next value
    • Definition at line 224 of file Encoder.h.
  • bool CEncoder::m_favorSpeed [private] β€” favor speed over size
    • Definition at line 226 of file Encoder.h.
  • bool CEncoder::m_forceWriting [private] β€” all macro blocks have to be written into the stream
    • Definition at line 227 of file Encoder.h.
  • int CEncoder::m_lastMacroBlock [private] β€” array index of the last created macro block
    • Definition at line 220 of file Encoder.h.
  • UINT32* CEncoder::m_levelLength [private] β€” temporary saves the level index
    • Definition at line 223 of file Encoder.h.
  • UINT64 CEncoder::m_levelLengthPos [private] β€” stream position of Metadata
    • Definition at line 215 of file Encoder.h.
  • int CEncoder::m_macroBlockLen [private] β€” array length
    • Definition at line 219 of file Encoder.h.
  • CMacroBlock** CEncoder::m_macroBlocks [private] β€” array of macroblocks
    • Definition at line 218 of file Encoder.h.
  • UINT8 CEncoder::m_nLevels [private] β€” number of levels
    • Definition at line 225 of file Encoder.h.
  • UINT64 CEncoder::m_startPosition [private] β€” stream position of PGF start (PreHeader)
    • Definition at line 214 of file Encoder.h.
  • CPGFStream* CEncoder::m_stream [private] β€” output PMF stream
    • Definition at line 213 of file Encoder.h.

🌸 Author 🌸

Generated automatically by Doxygen for libpgf from the source code. πŸ€–πŸ’–


and that's the entire page, bestie!! πŸŽ€ this iconic lil doc was auto-cooked by Doxygen for libpgf, version 7.21.2, dated Thu Jul 24 2025 00:00:00, and you just finished reading CEncoder(3). slay, you ate that βœ¨πŸ‘‘πŸ¦‹

πŸ“¦ where this page comes from