Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  my_compr_int.cc   Sprache: C

 

/*****************************************************************************

Copyright (c) 2024 Kristian Nielsen.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA

*****************************************************************************/


/*
  Reading and writing of compressed integers.

  Created 2024-10-01 Kristian Nielsen <knielsen@knielsen-hq.org>
*/


#include "mysys_priv.h"
#include "my_compr_int.h"

/* Read and write compressed (up to) 64-bit integers. */

/*
  Write compressed unsigned integer, efficient version without assuming
  unaligned writes.
*/

unsigned char *compr_int_write(unsigned char *p, uint64_t v) {
  // Compute bytes needed to store the value v plus 3 bits encoding length.
  uint32_t needed_bits_minus_1= 66 - my_nlz(v|1);
  uint32_t needed_bytes= (needed_bits_minus_1 >> 3) + 1;

  // Compute the encoding of the length.
  // We need 1-9 bytes. Use 9 bytes instead of 8, so we can encode the
  // length in 3 bits for (1, 2, ..., 7, or 9 bytes).
  uint32_t bytes= needed_bytes | (needed_bytes >> 3);
  uint32_t len= needed_bytes - 1;
  // Encode 1-7 as 0-6, and encode 8,9 both as 8.
  len-= (len >> 3);

  // Compute the first 64-bit word to write.
  uintptr_t offset= (uintptr_t)p & (uintptr_t)7;
  uintptr_t offset_bits= offset << 3;
  uint64_t v1= (len | (v << 3)) << offset_bits;
  uint64_t *p1= (uint64_t *)(p - offset);
  uint64_t mask1= ~(uint64_t)0 << offset_bits;

  // Compute the second word to write (if any).
  uint64_t v2= v >> ((64 - 3) - offset_bits);
  uint64_t *p2= p1 + 1;

  // Write the value into next one or two 64-bit words, as needed.
  // Two words are needed if (offset + bytes) cross into the next word.
#ifdef WORDS_BIGENDIAN
  /*
    Here it might be possible to use a slightly more efficient endian
    conversion on big-endian, since we know the pointer is 8-byte aligned.
  */

  int8store((unsigned char *)p1,
            (uint8korr((unsigned char *)p1) & ~mask1) | v1);
#else
  *p1= (*p1 & ~mask1) | v1;
#endif
  if (offset + bytes >= 8) {
#ifdef WORDS_BIGENDIAN
    int8store((unsigned char *)p2, v2);
#else
    *p2= v2;
#endif
  }
  return p + bytes;
}


/*
  Read compressed integer, efficient version without assuming unaligned reads.
  Returns a pair of the value read and the incremented pointer.
*/

std::pair<uint64_t, const unsigned char *>
compr_int_read(const unsigned char *p)
{
  uintptr_t offset= (uintptr_t)p & (uintptr_t)7;
  uintptr_t offset_bits= offset << 3;
  const uint64_t *p_align= (const uint64_t *)((uintptr_t)p & ~(uintptr_t)7);
#ifdef WORDS_BIGENDIAN
  /*
    Here it might be possible to use a slightly more efficient endian
    conversion on big-endian, since we know the pointer is 8-byte aligned.
  */

  uint64_t v1= uint8korr((unsigned char *)p_align);
#else
  uint64_t v1= p_align[0];
#endif
  uint32_t len= (v1 >> offset_bits) & 7;
  uint32_t bytes= len + 1;
  bytes+= (bytes >> 3);
  uint64_t v;
  if (offset + bytes > 8) {
    uint64_t mask2= (~(uint64_t)0) >> ((16 - (offset + bytes)) << 3);
#ifdef WORDS_BIGENDIAN
    v= (v1 >> (3 + offset_bits)) |
      ((uint8korr((unsigned char *)(p_align + 1)) & mask2) <<
       (61 - offset_bits));
#else
    v= (v1 >> (3 + offset_bits)) | ((p_align[1] & mask2) << (61 - offset_bits));
#endif
  } else {
    uint64_t mask1= (~(uint64_t)0) >> ((7 - (offset + len)) << 3);
    v= (v1 & mask1) >> (3 + offset_bits);
  }
  return std::pair<uint64_t, const unsigned char *>(v, p + bytes);
}


#ifdef TEST_MAIN
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


// Smaller version that assumes unaligned writes of 8-bit values is ok, and
// that there are up to 8 scratch bytes available after the value written.
unsigned char *compr_int_write_le_unaligned_buffer(unsigned char *p, uint64_t v) {
  // Compute bytes needed to store the value v plus 3 bits encoding length.
  uint32_t needed_bits_minus_1= 66 - my_nlz(v|1);
  uint32_t needed_bytes= (needed_bits_minus_1 >> 3) + 1;

  // Compute the encoding of the length.
  // We need 1-9 bytes. Use 9 bytes instead of 8, so we can encode the
  // length in 3 bits for (1, 2, ..., 7, or 9 bytes).
  uint32_t bytes= needed_bytes | (needed_bytes >> 3);
  uint32_t len= needed_bytes - 1;
  // Encode 1-7 as 0-6, and encode 8,9 both as 8.
  len-= (len >> 3);

  // Write the (up to) 9 bytes, prefering redundant write to conditional jump.
  *(uint64_t *)p= len | (v << 3);
  *(p+8)= v >> 63;
  return p + bytes;
}

// Generic version without assumptions.
unsigned char *compr_int_write_generic(unsigned char *p, uint64_t v) {
  // Compute bytes needed to store the value v plus 3 bits encoding length.
  uint32_t needed_bits_minus_1= 66 - my_nlz(v|1);
  uint32_t needed_bytes= (needed_bits_minus_1 >> 3) + 1;

  // Compute the encoding of the length.
  // We need 1-9 bytes. Use 9 bytes instead of 8, so we can encode the
  // length in 3 bits for (1, 2, ..., 7, or 9 bytes).
  uint32_t bytes= needed_bytes | (needed_bytes >> 3);
  uint32_t len= needed_bytes - 1;
  // Encode 1-7 as 0-6, and encode 8,9 both as 8.
  len-= (len >> 3);

  // Write the necessary bytes out.
  *p++= len | (v << 3);
  v >>= 5;
  while (--bytes > 0) {
    *p++= v;
    v>>= 8;
  }
  return p;

}


// Generic read compressed integers.
std::pair<uint64_t, const unsigned char *>
compr_int_read_generic(const unsigned char *p)
{
  uint64_t v= *p++;
  uint32_t bytes= v & 7;
  v>>= 3;
  uint32_t shift= 5;
  bytes+= ((bytes + 1) >> 3);  // A 7 means read 8 bytes more (9 total)
  while (bytes-- > 0) {
    v|= ((uint64_t)(*p++)) << shift;
    shift+= 8;
  }
  return std::pair<uint64_t, const unsigned char *>(v, p);
}


// Read compressed integers assuming little-endian, efficient unaligned
// 64-bit reads, and up to 7 bytes of scratch space after.
std::pair<uint64_t, const unsigned char *>
compr_int_read_le_unaligned_buf(const unsigned char *p)
{
  uint64_t v= *(const uint64_t *)p;
  uint32_t len= v & 7;
  uint64_t mask= (~(uint64_t)0) >> ((7 - len) << 3);
  v= (v & mask) >> 3;
  // Need for extra read is assumed rare, well-predicted conditional jump
  // likely faster.
  uint32_t bytes= len + 1;
  if (__builtin_expect((len == 7), 0)) {
    v|= ((uint64_t)p[8]) << 61;   // Add last 3 bits
    bytes+= 1;                    // 7 means read 9 bytes
  }
  return std::pair<uint64_t, const unsigned char *>(v, p + bytes);
}


int
main(int argc, char *argv[])
{
  int N= (argc > 1 ? atoi(argv[1]) : 1000);
  uint64_t *src= new uint64_t[N];
  unsigned char *buf1= new unsigned char[N*9];
  unsigned char *buf2= new unsigned char[N*9];
  int i;

  // Generate test data.
  for (i= 0; i < N; ++i)
    src[i]= ((uint64_t)1 << (rand() % 64)) + (uint64_t)(rand());
  // Write test data.
  unsigned char *p1= buf1;
  unsigned char *p2= buf2;
  for (i= 0; i < N; ++i) {
    p1= compr_int_write(p1, src[i]);
    p2= compr_int_write_generic(p2, src[i]);
  }
  if ((p1 - buf1) != (p2 - buf2)) {
    fprintf(stderr, "Write error! Mismatch lengths of optimised and generic.\n");
  } else {
    if (memcmp(buf1, buf2, p1 - buf1))
      fprintf(stderr, "Write error! Mismatch data of optimised and generic.\n");
  }
  // Verify written data.
  std::pair<uint64_t, const unsigned char *>q1, q2;
  const unsigned char *c1= buf1;
  const unsigned char *c2= buf2;
  for (i= 0; i < N; ++i) {
    q1= compr_int_read(c1);
    q2= compr_int_read_generic(c2);
    uint64_t v1= q1.first;
    c1= q1.second;
    uint64_t v2= q2.first;
    c2= q2.second;
    if (v1 != v2 || v1 != src[i]) {
      fprintf(stderr, "Read error! mismatch values @ i=%d 0x%llx 0x%llx 0x%llx.\n",
              i, (unsigned long long)v1, (unsigned long long)v2,
              (unsigned long long)(src[i]));
      break;
    }
  }
  return 0;
}

#endif  /* TEST_MAIN */

Messung V0.5 in Prozent
C=84 H=91 G=87

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-10-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1867298