Skip to content

Latest commit

 

History

History
1491 lines (1271 loc) · 42.2 KB

README.org

File metadata and controls

1491 lines (1271 loc) · 42.2 KB

ifcidc: A literate IFC GUID (de)compression library

https://travis-ci.org/devonsparks/ifcidc.svg?branch=master

Context

If you work with IFC files in any depth, you’ll inevitably confront IFC’s slightly unusual GUID encoding scheme. If you’re used to authoring applications like Revit, you might expect IFC GUIDs to follow the canonical UUID encoding for unique 128-bit numbers. Something like:

b29d2e4d-9209-4ef1-aa55-9df70bf727fe

Instead, you’ll find 22-character ASCII character strings, like the first argument to this IfcBeam instance:

#23359= IFCBEAM('2odIvDaWbEyQfLdVSBzoV_',5,'ANCHOR ROD','RB1 1/2''','RB1 1/2''',23356,23358,'ANR0(?)');

These 22-character strings represent the same GUID entity, albeit in a compressed form. It’s worth understanding how these two encodings map to one another and to translate this understanding in a computer program.

To start, let’s set the scene. Many CAD authoring applications represent GUIDs as 128-bit numbers using the standard UUID encoding scheme defined by RFC 4122. This encoding follows strict rules. The UUID’s 16 octets are represented using 32 hexidecimal digits displayed in five groups separated by hyphens. This implies four bits per character. We can map between these base-16 digits and their familiar base 10 representions using a lookup table:

01
0123456789012345
0123456789ABCDEF

Mapping the same 128-bit number to the encoding seen in IFC files requires similar lookup table, this time using 22 base-64 digits (6 bits each), and provided as part of the IFC standard:

01
01234567890123456789
0123456789ABCDEFGHIJ
23
01234567890123456789
KLMNOPQRSTUVWXYZabcd
45
01234567890123456789
efghijklmnopqrstuvwx
6
0123
yz_$

Converting between the two encodings requires two tasks:

  • Asking how many groups of four bits fit in a sequence of 22 base-64 characters and vice versa. Let the number of bits in the resulting sequence be S.
  • Partition the 128 bits of the original GUID into groups of 128/S. For each group, take N bits at a time, where N = 6 for compression, N = 4 for decompression, and use the associated lookup tables to convert the N-bit number into its associated character encoding. The resulting characters make up the encoded GUID.

Solving the first task is easy. Because S = LCD(4,6) = 12, 12 bits is sufficient to hold both three 16-bit characters or two 64-bit characters. More formally, for any two 64-bit numbers A1, A2 and three 16-bit numbers B1, B2, B3, the following relationship holds: A1*2^6 + A2 = B1*2^8 + B2*2^4 + B3.

The second task requires a little more thought. Because (128 mod 12) ≠ 0, one of the 128/S groups will be truncated. To solve this, we could choose

  1. to add an additional four bits to our UUID buffer to support byte aligned operations (because 132 mod 12 = 0), or
  2. have some additional logic to handle the corner cases at the buffer boundary.

We opt for the former case, believing that the need for a working buffer is a fair price for a closer mapping between our reasoning and the resulting code. We can use the formal relationship derived above to perform the (de)compression for all 11 12-bit groups in the 128 + 4 = 132 bit sized buffer.

Implementation Preliminaries

We’ll apply our strategy for IFC GUID compression in the creation of a simple library to automate the process. Every library needs a name. We’ll call ours ifcidc, an uncreative abbreviation for “IFC (gu)ID Compressor”.

At minimum, ifcidc needs two public procedures – one to compress an IFC GUID and one to decompress. Naturally, they are duals, so for a string L, decompress(compress(L)) = L. We imagine each of these procedures taking two arguments: the first, in, is the GUID string to be processed, and the second, out, is a buffer to store the generated dual GUID. Populating the result of the (de)compression in a buffer passed by argument frees us to use the procedure’s return type for status notification (e.g., OK, FAILED).

#ifndef _IFCIDC_H
#define _IFCIDC_H

<<H_Constants>>

typedef enum  {
    <<H_Statuses>>
} IFCIDC_Status;

<<H_Declarations>>

#endif
IFCIDC_Status 
ifcidc_compress(const char *in, char *out);

IFCIDC_Status 
ifcidc_decompress(const char *in, char *out);

Three constants are likely to come in handy throughout our program: the length of an uncompressed UUID string (including hyphens), the length of the “normalized” uncompressed UUID string (without hyphens), and the length of the compressed GUID string. Let’s make those available from the start so we’re less likely to pollute the source file with magic numbers.

#define IFCIDC_DECOM_LEN       (36)
#define IFCIDC_FIXED_DECOM_LEN (32)
#define IFCIDC_COM_LEN         (22)

We don’t know what kinds of errors we could trigger until we get further into development, so to start we brazenly assume every (de)compression invocation returns success.

S_OK = 0,

Core Implementation

Our library implementation will follow a typical structure:

<<Headers>>

<<Macros>>

<<Declarations>>

<<Definitions>>
<<Standard-Headers>>

#include "ifcidc.h"

Creating the lookup tables

Our first task is to create programmatic versions of the lookup tables we defined during our initial discussion. These tables need to provide bidirectional lookup: given an index, return the associated character, and given a character, return the associated index.

Doing this in the forward direction (index to characters) is easy: just create a character array for each table.

static const char *
b64 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";

static const char *
b16 = "0123456789ABCDEF";

Converting characters to table indices requires a bit more work. We assume our incoming data will be filtered to only provide ASCII data to the compression procedures (we’ll enforce this later). That means there are 128 possible input characters to be used as indices in our “backwards” lookup table. If we create a 128 character array, one entry per ASCII character, we can store the indices of those characters into the complementary lookup table in each cell. For example, because ASCII ‘A’ has decimal value 65, and it is located at index 10 in the forward base-64 lookup table, we store 10 at index 65 in the complementary lookup table. We do this for every character in the forward lookup tables. Because array indices are never negative, we use any negative number to indicate that the given ASCII character is not present in the complementary lookup table.

static const char
b16mask[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, \
	     -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} ;


static const char
b64mask[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	     -1, -1, -1, -1, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, \
	      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, \
	     -1, 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, -1, -1, -1, -1, 62, \
	     -1, 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, -1, -1, -1, -1, -1 };

This approach is not the only way to solve the problem. We could have created these bidirectional lookup tables with some conditional logic. Instead, we opted to use complementary index arrays because they provides constant-time lookup, small code size, and a close mapping to our conceptual model. This will work so long as we provide bounds checking on the array indices.

We can wrap lookups and bounds checking into macros, one for each lookup table:

#define B162I(A)  (b16mask[(unsigned char)A])
#define B642I(A)  (b64mask[(unsigned char)A])
#define IN_B16(A) (!(b16mask[(unsigned char)A] < 0))
#define IN_B64(A) (!(b64mask[(unsigned char)A] < 0))

Compression Implementation

We now have enough scaffolding to implement our core compression algorithm. We assume a private (static) procedure that takes in our uncompressed UUID (normalized with one byte padding and without hyphens) and modifies its second argument to produce the compressed equivalent.

static IFCIDC_Status
com(const char *in, char *out);

Similarly for our core decompression algorithm:

static IFCIDC_Status
decom(const char *in, char *out);

The algorithm comes directly from our preliminary discussion: for every 12-bit sequence in the input string, extract the base-16 number it represents, then compute the base-64 character equivalents and store them in the output string.

static IFCIDC_Status
com(const char *in, char *out) {
  int i,oi, n;
 
  i = oi = n = 0;
  while(i < IFCIDC_FIXED_DECOM_LEN) {
    n  = B162I(in[i    ]) << 8;
    n += B162I(in[i + 1]) << 4;
    n += B162I(in[i + 2]);
    out[oi + 1] = b64[n % 64];
    out[oi    ] = b64[n / 64];
    oi += 2;
    i  += 3;
  }
  out[oi] = '\0';
  return S_OK;
}

The same approach works for decompression. The code intends to reflect that compression and decompression are dual operations.

static IFCIDC_Status
decom(const char *in, char *out) {
  int i, oi, n, t;

  i = oi = n =  0;
  while(i < IFCIDC_COM_LEN) {
    n  = B642I(in[i]) << 6;
    n += B642I(in[i + 1]);
    t  = n / 16;
    out[oi + 2] = b16[n % 16];
    out[oi + 1] = b16[t % 16];
    out[oi    ] = b16[t / 16];
    oi += 3;
    i  += 2;
  }
  out[oi] = '\0';
  return S_OK;
}

Because in and out are string buffers, it would help to have dedicated constructor and deconstructor procedures for them. These buffers can be of a fixed size so long as they’re bigger than IFCIDC_DECOM_LEN.

IFCIDC_Status 
ifcidc_buffer_new(char **buf);

void 
ifcidc_buffer_del(char *buf);
#define BUFSIZE 80
#include <stdlib.h>
#include <assert.h>
IFCIDC_Status
ifcidc_buffer_new(char **buf) {
  assert(BUFSIZE > IFCIDC_DECOM_LEN);

  if((*buf = malloc((BUFSIZE) * sizeof(char))) == NULL)
     return S_ERR_MEM;

  memset(*buf, ' ', (BUFSIZE) * sizeof(char));
  (*buf)[BUFSIZE - 1] = '\0';
  return S_OK;
}

void
ifcidc_buffer_del(char *buf) {
  if(buf) free(buf);
}

Because we’re allocating from the heap, we’ll also need a new status type for insufficient memory.

S_ERR_MEM,

String Munging

Our implementation strategy requires some munging of the uncompressed UUID string. Namely, that we have to remove the hyphens from the incoming string and add an extra byte of padding to the start of the string to support byte aligned operations. We’ll call the procedure that performs this operation fixid, and its dual, unfixid. They’ll use the same in-/out/ argument conventions as the core compression procedures.

static IFCIDC_Status
fixid(const char *in, char *out);

static IFCIDC_Status
unfixid(const char *in, char *out);
static IFCIDC_Status
fixid(const char *in, char *out) {
  unsigned int i, j;

  out[0] = '0';
  out[IFCIDC_FIXED_DECOM_LEN + 1] = '\0';
  
  for(i = j = 0; in[i] != '\0'; i++) {
    if(in[i] != '-') {
	out[++j] = in[i];
      }
  }

  assert(j == IFCIDC_FIXED_DECOM_LEN);

  return S_OK;
  
}

unfixid just needs to reverse the work done by fixid: adding the hyphens back in at indices 8, 13, 18, and 23, and removing the leading byte padding we used in the working buffer.

static IFCIDC_Status
unfixid(const char *in, char *out) {
  unsigned int i, j;

  out[IFCIDC_DECOM_LEN] = '\0';

  for(j = 0, i = 1; in[i] != '\0';) {
    if(j == 8 || j == 13 || j == 18 || j == 23) {
      out[j++] = '-';
    }
    else {       
      out[j++] = in[i++];
    }
  }

  return S_OK;
  
}

Now we have core compression algorithms and normalization helper procedures. All we have to do is wrap them in our public interface and address the possible failure modes on the input data.

IFCIDC_Status
ifcidc_compress(const char *in, char *out) {
  char comed[IFCIDC_FIXED_DECOM_LEN + 1];
  unsigned char i;
  
  <<Check-Compress-Input-Length>>
  <<Check-Compress-Input-Sentinel>>
  <<Check-ASCII-Compliance>>
  <<Check-fixid-Success>>
  <<Check-Compression-Success>>    

  return S_OK;
}

We’ll need one IFC_Status for each of the possible failure modes. Let’s add them now.

S_ERR_INPUT_LEN,
S_ERR_SENTINEL,
S_ERR_ASCII,
S_ERR_NORMALIZE,
S_ERR_COM,

We’ll use strlen to check the input length.

#include <string.h>

For the failure modes themselves:

if(strlen(in) != IFCIDC_DECOM_LEN) {
  return S_ERR_INPUT_LEN;
  }
if(in[IFCIDC_DECOM_LEN] != '\0') {
  return S_ERR_SENTINEL;
}
for(i = 0; in[i] != '\0'; i++) {
  if(in[i] != '-' && !IN_B16(in[i])) {
    return S_ERR_ASCII;
  }
}
if(fixid(in, comed) != S_OK) {
  return S_ERR_NORMALIZE;
}
if(com(comed, out) != S_OK) {
  return S_ERR_COM;
}

The public GUID decompression procedure is similar enough to its compression counterpart that we present the failure modes inline. The one exception is that the decompression and normalization calls are reversed, for the simple reason that we can only “denormalize” a string after we’ve decompressed it.

IFCIDC_Status
ifcidc_decompress(const char *in, char *out) {
  char decomed[IFCIDC_FIXED_DECOM_LEN + 1];
  unsigned char i;

  if(strlen(in) != IFCIDC_COM_LEN) {
    return S_ERR_INPUT_LEN;
  }
     
  if(in[IFCIDC_COM_LEN] != '\0') {
    return S_ERR_SENTINEL;
  }

  for(i = 0; in[i] != '\0'; i++)
    if(!IN_B64(in[i]))
      return S_ERR_ASCII;
  
  
  if(decom(in, decomed) != S_OK) {
    return S_ERR_COM;
  }
  
  if(unfixid(decomed, out) != S_OK) {
    return S_ERR_NORMALIZE;
  }

  return S_OK;
}

Error Interpretation

Finally, we’d like our error codes to have some human-readable interpretation. For this, we create a mapping between error codes and error messages.

static const struct
_errordesc {
  int  code;
  char *message;
} errordesc[] = {
  { S_OK,            "Compression successful." },
  { S_ERR_INPUT_LEN, "Unexpected input length."},
  { S_ERR_SENTINEL,  "Expected string sentinel not found."},
  { S_ERR_ASCII,     "Non-ASCII character found in input."},
  { S_ERR_NORMALIZE, "Unable to normalize input string."},
  { S_ERR_COM,       "Unable to perform compression operation."},
  { S_ERR_MEM,       "Unable to allocate memory."}
};

This permits us to write a utility function to look up an error message from a given error code.

char *
ifcidc_err_msg(IFCIDC_Status err);
char *
ifcidc_err_msg(IFCIDC_Status err) {
  unsigned short es;

  es = sizeof(errordesc)/sizeof(struct _errordesc);
  while(es-- > 0) {
    if (errordesc[es].code == err) {
      return errordesc[es].message;
    }
  }
  return "";
}

This completes ifcidc’s interface specification. We now can merge all this into a shared library.

Building the IFCIDC shared library

We’ll use a makefile to control library compilation. This allows us to produce a dynamic library, libifcidc.so, for use in client applications.

.PHONY: default
default: all

<<Tooling-Variables>>

<<Real-Targets>>

<<Phony-Targets>>
srcdir=src
bindir=bin
incdir=inc
libdir=lib

cc=LD_LIBRARY_PATH=$(libdir) gcc
cflags=-I$(incdir) -Wall -g
$(libdir)/libifcidc.so: $(srcdir)/ifcidc.c
	mkdir -p $(libdir)
	$(cc) $(cflags) -shared -fPIC -o $@ $^
all: $(libdir)/libifcidc.so

Writing a Client Application

We now use our library, libifcidc.so, in an example command line utility for IFC GUID compression. We call this utility ifcc, a play off “cc” for “compression” or “compilation”.

First, we set up our overall program structure.

<<Client-Headers>>

<<Client-Declarations>>

<<Client-Toplevel>>

<<Client-Definitions>>
<<Client-Standard-Headers>>

#include "ifcidc.h"

We imagine our utility reading in GUIDS, one per line, from a file (which may be stdin). We indicate this file with a flag, -i. For instance, ifcc -i guids.txt. We redirect output of the processed GUIDs with a complementary flag, -o. If -i is left off, the utility takes input from stdin; if -o if left off, output goes to stdout. We indicate compression/decompression operations with -c and -x flags respectively.

The core feature of such a utility will be the subroutine to process the lines of the input file. For this, we’ll need access to the input and output FILE pointers, a handle on the compression algorithm to run (ifcidc_compress or ifcidc_decompress) and index variables so we know how many characters to read from the input and streams before adding a sentinel (36 for compression, 22 for decompression). This description easily leads us to the following declaration:

static IFCIDC_Status
process_lines(FILE *fip,
	      FILE *fop,
	      const unsigned short si,
	      const unsigned short so,
	      IFCIDC_Status (*processor)(const char *in, char *out));

The internal structure of this routine can be simple. We’ll need pointers for our input and output buffers as well as an IFCIDC_Status variable to check for success processing each line. If we’re reading GUIDs from files, we’ll need stdio. We’ll use unistd’s getopt for argument parsing, and stdlib’s definitions of EXIT_SUCCESS and EXIT_FAILURE.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static IFCIDC_Status
process_lines(FILE *fip,
	      FILE *fop,
	      const unsigned short si,
	      const unsigned short so,
	       IFCIDC_Status (*processor)(const char *in, char *out)) {

    IFCIDC_Status s;
    char *in, *out;

    if((s = ifcidc_buffer_new(&in)) != S_OK)
      return s;
    if((s = ifcidc_buffer_new(&out)) != S_OK)
      return s;
    while (<<process_lines-More-Lines-To-Read>>) {
      <<process_lines-Process-A-Line>> 
    }

    ifcidc_buffer_del(in);
    ifcidc_buffer_del(out);
    return S_OK;
}

For line reading, we opt to use fgets, reading from a line until we reach the number of characters needed for the (de)compression operation or hit a newline – whichever comes first. si + 1 + 1 indicates that we shouldn’t read more characters than those needed for the GUID plus a sentinel and newline character.

fgets(in, BUFSIZE, fip) != NULL

Once we’ve read a line, we can pass it directly to the compression processor. Just be mindful that this could fail!

in[si] = '\0';
if((s = processor(in, out)) != S_OK) {
   ifcidc_buffer_del(in);
   ifcidc_buffer_del(out);
   return s;
}
else {
   fprintf(fop, "%s\n", out);
}

Equipped with our line processior, we can compose the toplevel of our client application. It’s simple enough that we present it inline.

int
main(const int argc, char *argv[])
{

  char *fin;
  char *fon;
  FILE *fip;
  FILE *fop;
  int opt;
  unsigned short com;
  IFCIDC_Status status; 

  com = 1;
  fin = NULL;
  fon = NULL;
  fip = stdin;
  fop = stdout;
  while ((opt = getopt(argc, argv, "cxi:o:")) != -1) {
    switch(opt) {
    case 'c':
      com = 1;
      break;
    case 'x':
      com = 0;
      break;
    case 'i':
      fin = optarg;
      break;
    case 'o':
      fon = optarg;
      break;
    default:    
      break;
    }
  }


  if(fin != NULL) {
    if((fip = fopen(fin, "r")) == NULL) {
      fprintf(stderr,"Failed to open file %s\n", fin);
      return EXIT_FAILURE;
    }    
  }


  if(fon != NULL) {
    if((fop = fopen(fon, "w")) == NULL) {
      fprintf(stderr,"Failed to open file %s\n", fon);
      return EXIT_FAILURE;
    }    
  }

  status = (com == 1) ?
    process_lines(fip, fop, IFCIDC_DECOM_LEN, IFCIDC_COM_LEN,   &ifcidc_compress)   :
    process_lines(fip, fop, IFCIDC_COM_LEN,   IFCIDC_DECOM_LEN, &ifcidc_decompress) ;

  fclose(fip);
  fclose(fop);

  if(status != S_OK) {
    fprintf(stderr, "%s: %s\n", argv[0], ifcidc_err_msg(status));
    return EXIT_FAILURE;
  }
  
  return EXIT_SUCCESS;

}

Finally, let’s add the client application to our build rules.

$(bindir)/ifcc: $(srcdir)/ifcc.c $(libdir)/libifcidc.so
	mkdir -p $(bindir)
	$(cc) $(cflags) -L$(libdir) -lifcidc -o $@ $^
all: $(bindir)/ifcc

Testing the Client Application

To test the client, we run it against known GUIDs in their compressed and uncompressed form. We present two test files for this use. The first contains 256 uncompressed IFC GUID strings; the second, the same 256 IFC GUIDs in their compressed form. These have been produced by an externally validated tool.

exdir=ex
3085A8E4-61FD-4776-9FF1-1B24A646CA4F
12197C0B-DFA7-4C19-B3E6-D1A9A59663AB
163EFED7-1B9A-4E8C-B69A-6497F95C232A
182CA57E-0C83-48CC-A47A-8514F066DEB2
9BB6C3B7-A412-41A9-9851-4BCCB0ED0526
5B2350DF-CE5E-482F-9ACF-0BCA06C4E4D7
E862FFB0-6793-4340-AD5E-62D18A70F9E3
C88129C7-3F45-46E8-AFFF-EAC60FEDE0BE
4C155151-2642-4FA1-AB0A-80D2BAF5FA89
B84A4E66-97FB-4FA2-B1FE-2B230171170C
8F64282D-4B0B-4A75-B798-254A54A59DEF
66BDA932-AC14-40B8-BA02-630349561ABF
59EB0A89-2B10-4A22-A642-EEC4049B831E
348771A0-2E84-4C24-96D4-6780F633D002
A9B50BD5-0EBA-4072-AC63-FC7484577A3E
D3D61E31-4CD8-4C08-8C0A-9A278AAA9B4E
4E115A05-2D0B-4976-B811-F724FC399C41
6616483D-07E3-4E27-90B5-A54C50440E32
0E6A8F85-27C8-4BF1-9F07-87118918D480
00C279B5-E1E6-476E-B087-A9777B77253E
B74FE921-BB2E-44F9-8A83-F5AAAC62108D
1C6CBEF4-2F51-4058-B02A-B5AA22DFEA42
2FFE4C67-12C2-488E-82DB-996638F29770
233E7E68-1E33-4AA0-97DD-E6D485CF8E6E
114ED2EB-CB57-43D3-B252-CEE041FCDC1F
BE9DDB66-B4DC-45CE-8BCB-FEA9A3A71E69
88778FDD-F823-4B90-A68A-B1C14BD9E356
AD207452-482C-44CA-9070-8ADE649A16ED
4D8F7313-D06E-45E4-8182-5E2F4F81FE48
01973BAE-7066-4702-BFC9-486D7637201C
8EA2CDD2-6662-41CE-863C-6AD8840C03DB
3FAF53B7-E9AB-4D5C-916A-2344C7E829AA
B4CECE78-400D-4C78-AF3D-3B4865BE9D2E
1D0E7B96-27C3-4388-86D1-349B297969F7
13CBD3A9-D450-4120-A6EE-40B0AD3F4BA3
09702D64-0121-49DD-B851-ADA9A847C222
277DDB9B-0F7C-4CD8-9962-A1D589B386B9
9E4FA830-373E-4B4A-AFC8-92ED51CFC007
4D5F5AC0-E636-425A-B05E-9FDC9F2DDE6A
DDE8E6EA-BDEB-4920-8601-F2BDEB402B0D
75145D75-AF9D-4BF4-BE33-25686C148FF5
7243DF56-F446-4BAC-8D1F-FAFD8F5DE9BA
4A83EE5D-6C43-42F0-9E3E-4704A0BEE2E1
90ABDB53-553B-4F0A-B977-196F767CCD2B
F93A9570-7AD6-465E-B00B-E76451F5B4F6
13519721-935D-4DFA-B5C4-700185639895
B91206A2-D5E6-47C3-BBC6-333D02899BD2
1E0D8434-5C90-4738-921E-048791073B7B
94584F5D-47BE-4B50-9700-4744407CF46D
E193E5BD-83ED-4C63-92A8-0180FCB53C7F
E8A22D9E-DB58-4B60-B122-6A9D202F1852
BD47B7CE-3DF0-450E-878C-AED5C46000F3
C038590D-CB2A-41A3-88F5-CAC3A30147DE
AD574B6C-9EFA-4B64-8F39-E7D54C36AF4D
902C52A9-EC73-4C60-A96C-6E256E9FE662
2F3A801D-138E-479D-948E-BFF44085FBA4
92960212-3D88-42C8-9518-37B4215B5807
1BE6F6BF-1270-44E4-B35C-56007CB9C3E7
BA45794C-C44E-4059-AC31-938121C6AA01
52D531C6-400F-4BB4-9FB7-0EC3A331F5CB
2F5AD2D1-D3ED-4F08-971D-9E3F8C949CE0
DD0F41B0-8604-4662-9DFB-95F8B9026716
688D849F-9755-4F5D-B6D9-787AC4695F1C
25643E0E-3AED-484D-B302-8B1C75CA7ADC
2E1B9BDE-12E5-427D-92CE-21E87213DB8A
536D9E3D-EE5B-43C6-9BF2-E2F965151C5F
34CBA891-F557-41A4-A172-BD8558C13ADC
46B92BA0-01FD-4309-88E3-F2364DB3B024
BDB2AC77-E807-4F0B-B031-D8E8997BA3FC
794BDA5D-0AF7-45B1-9AEF-171004AB4630
D28EA8B0-9026-4534-934F-C7D1786E56F5
4ADBF3B4-4BD0-46E8-B398-C87E1567BAE4
6CAFF339-4EB0-410B-9D55-233B541AEF14
F8D3E203-3807-4BAD-A110-3300692A9782
8E736F9D-243B-4EDC-A9A7-226C2B6585FD
4039E7F9-4C49-4B9D-B1C5-1A6B5256E578
8A57C7EC-16DF-4B77-B3FD-6CE284AFC0C8
933C3E03-B196-463F-892E-316A293AF0DE
08B3FABE-D4A3-4E1C-A2F1-2A0B41B2240C
26E330B9-FFAE-48C5-AE7E-DBAC6D514C05
72D39AA1-ABF0-46DE-ADC4-F941707CB3A3
8383D0A9-A246-47CF-B228-F777AE26E793
0D432EC4-BECE-4008-93AF-5FAC9D4D8954
5B78B068-0F9E-4372-A8CF-F5E56673BFC3
411BD9F8-6D2F-4423-8851-10DBBD34E862
238C27DE-79F0-436F-A0CD-503BDB1B8C70
13B109F8-3BB8-4A67-BDA5-88169ED8F71E
AA4393DD-A1AD-43E9-B703-71D763166FF8
27E6A1FA-72E9-4C79-AFA9-9D3DC748BB33
25C689FD-2C12-4203-B3E0-244616A4C4BE
B574A45B-5870-44B1-8405-3DB62FEEA488
6E7AA299-9FBC-4716-ACDA-32F64A79F8DD
B68BED65-91C1-48F3-9B4F-019DBBCC4390
FD811263-2B8C-4DA1-991D-A58FD9FA6791
AB8C6E0D-0E70-449A-8618-76324E8549CE
436B2BF4-17FC-417A-A58B-9173BE02CA72
ECC05584-4989-4E64-9A65-5829AC478397
D783F488-1E65-4A34-80B3-956A6E9090E5
EFEE0A6F-2638-4694-804F-D2F08D1E7F7B
11AD6281-B704-44EB-82EE-59337F5BDBA5
08A20574-612F-43E7-B675-0C9C4C64446C
FAFE2D42-3D32-4411-B5B3-E9B467CDBF0B
E8EB1BFC-A398-4B19-8DFA-6F53B62FA11F
04B3B6FE-14C4-462C-B923-E6D336565F87
BD372319-877E-408F-A97D-0B63ECA20478
27B93FD7-D506-4FC5-B306-1A3DB57B9363
167F99F6-7C6C-4C92-B99E-2A0A372D2FA0
04E3AC5F-710D-43B1-A989-9020FC84F331
647F1044-E6AB-4D3C-A952-F98FC7E23502
748774AF-6751-411D-AD06-F51D7E85965F
7DAFE0FA-6768-4269-B99B-1649873FD3E8
03DFFB93-C814-4CB6-AFC0-9C7FC71A11D5
CB9456F9-1826-4C63-A81D-6B0F30232DFC
F62F35A0-8CE3-48DF-A9BA-1D771CE44E66
3C5D0C4D-C359-4231-BAB4-B425994DC0C8
42B64AFF-61CD-4F41-83D2-1B963FF66834
400A4619-C518-4EB7-91A4-93D012D49A1B
E4C45EEC-8C7C-483B-9FFD-1D5A5A25AF5B
2FE9508B-F512-49B7-B750-D97EA8F2B8A4
57088090-F6A0-4100-A666-9AB72178E1B2
529266FF-4F13-41A5-A050-185A936EEF80
FBD923A9-465F-4779-AE53-BD813547B00E
CEA96A19-3BC4-4869-959C-A897D7618118
C0EA9E99-E44F-4FBC-9C83-F040804687E5
91E0F572-C6D7-4A05-8582-A1FFB2ED617C
FEB5765F-C8E4-44A4-96C5-70101260B441
73850CCD-6FC6-4A64-A844-A2BC6CF58E8A
ED60B83B-BADA-464B-A50D-18807EFD8A9E
76E963F8-7A60-4CAA-A4C7-3433BC7415A3
9CE8366E-6318-4E0D-847B-5352225B0A28
2410B058-EC42-4E79-A526-E4FE4267D391
BDE89939-EB73-48A3-A85F-76A170F870D0
E5C6C90A-025F-4667-A2F9-AA082A0A81C6
CF84935B-C0B3-416B-8285-E6009318BBF6
71C11D4B-EE24-4D6F-AFDE-A7F86D8C9DF2
CF224B37-B0A2-4556-A6EF-04A06E6D22FD
7489BD9E-BDDF-47EF-9E29-A864E38BE43A
6624AE3E-5680-485E-AA98-FB3845CDE132
C2112D4D-E307-45C7-B42A-04E94795C824
25A1BD60-EFBB-4A23-995E-5A8E578CD3E6
47C94809-84F2-47D3-8550-3D9A934BFB5B
61869E89-274D-4635-A499-EF1E7F9EF3D7
C717E0C8-91FB-43EA-B083-DDDDAF74AFD6
BC7E5E5C-6DA8-4D1B-9396-B554D5F79134
596FC212-0654-47D9-9719-DE402EB1B290
FEFC1E52-57EF-4BD9-8187-76C534D202C7
440965CE-8D3F-43F7-BCA6-6C283160A413
02210FFF-0D41-43DB-8170-06F57B26A279
BF19D723-6C40-4517-9DD6-4C64CACE5653
355A9131-8615-4789-BEF3-7929ED778CC9
D157D5F8-0442-4F11-B6B1-D541D6768729
01ABD14B-289B-4929-81D4-F1A23FA2D51B
59908B2A-6F80-46D0-8DCB-96C7E3A311D3
E4D34551-0479-44B3-BA45-EEBA9F02AC37
AA8DA04D-D32A-4BB8-A76F-0E2102016D4F
8F110226-20A5-4F88-A841-B68D12C7606F
8A4506F8-9277-40DB-9576-B6D9E7D67C76
B8148D71-A0C1-4666-8D65-8FCBB118845E
F1BB68AE-CE2D-4749-AB43-E177A9A6DAD7
B84FCE5D-F4C9-4D03-A05E-1C3EB6E53C02
EC722D42-742D-4EBF-A649-82D3F5996297
8DD86B8A-43A9-4291-A343-989A3452FCAA
42F97FB6-16A8-4B15-9AF3-51B2ED9C0196
1291AD5B-2007-47E3-A85A-8EADDA067CCA
250A7F16-0080-4067-A24A-9E6A707B94A8
983D81C9-3F2E-4B79-9433-0E72458811FD
1D73DBE9-4CBA-450B-B6B7-4F781B48957A
DD8ACB75-266D-421C-9F28-03FB65B47E17
9F87A3B7-C6A6-4E49-ACFD-619027BC5399
01F528B8-1360-4F02-84BE-9BCE127B3BDA
B0079989-A6DA-45CE-BC95-ED4B6DF42EE3
219CF4BA-08CA-412E-BF7B-D3FF728314D8
CAC10F31-4194-4A99-A887-B1BE609FDEBA
81B28414-D19A-41AF-8550-3AE0A8861CA0
FC664F9C-263C-4F0E-9D43-00DA78E5403B
A346E04E-DF77-477E-93C9-27C83B6B4706
607958F2-9582-4320-9A4A-DDB10A0E55CD
2A6352B8-3FBC-476E-96DC-6851883F215A
14462A71-7CB9-41B3-955C-E0B2486FDD92
406BE8FE-2084-456A-82CF-D930E82615B0
60C013D3-5324-497E-B4C7-264610981235
4F379773-EBD6-45CF-A342-227A88B83EA0
240603EB-A928-40E6-83A5-F5D4FDCA1C7B
21D4B317-7ECA-48F0-A903-A00F7CDFBD52
3E3C5D6D-22D1-4653-AF7D-C51E92E1A016
03842665-771F-4C8A-88E4-BE1BDE6117EB
632E586C-BF9C-47D9-94B6-D12DF92DCD54
86B4F76F-484F-4C82-899F-EE4BAAA7806D
EF379F1C-034C-4918-81BF-D3A26378460C
676B2C07-1914-4734-B3FE-5BCA665E3965
57EE9C3A-22D3-41B6-95EF-29101194FAEE
A4A6727D-A4F2-4380-A31A-17C1D7A8BFCE
FC101646-8CE3-42BD-B41A-46DCCAFC940C
B760E6C5-FE70-48BF-9C5D-933B0FBD03CC
63824448-2CAC-41FF-98C9-E0481161325A
D6561D74-DDA7-4AAE-B0C9-BBAB4A861B42
93BA81AF-164C-41CE-BE7A-1B53B1A34197
F62D9960-7344-4EEE-98C4-43306A0DD63D
41B8BBCA-A459-4635-BD5D-AE2C61CD42D7
6674C8B6-3EDC-4DB5-9620-BA0DBACE18C3
5E25CC1D-F4CF-4CC7-85E3-B28855992431
2DA1095D-E32A-421C-B0AA-5D4934FEBB46
DC7B7FD2-9E36-446A-AD1F-7CDD6E5D58F6
E067931B-3733-46B4-96E6-95E2CDF34963
7F8D6777-ACB5-4935-8828-345746B210D4
521C6B69-8CE4-4714-A44C-641679D8F801
DF340BCF-F292-40B5-8826-A0D5A64FA664
9E53D3B9-8F02-4A23-9092-9F51B5970F86
D53E4BAB-D127-4A52-B8DA-05908EF77645
6F18F6B4-C970-4726-BDDA-02DA0078F7AC
E929F69C-234A-475F-9ED8-843A93866401
CEF97CFA-EE61-4795-9CDC-F8E0F0F88AB6
6E062D7A-98C8-4295-BB16-F2D1AA1C162E
C3FF8931-8304-479C-8300-CED33D6A32FF
BAA1CC9B-4616-4932-960A-9697A5E9EE99
C123100E-D157-4391-B904-6DAF7DC6D4BA
32839206-8FEA-45E3-B03C-953A432D3D1B
7D2A3843-0322-44E3-8309-11CE37129DB5
CBDBF3B4-3CB6-47B6-B00E-BAD9E7A26318
0CDFD173-91B9-4EDC-9F3C-6B8B9045F8F3
C501E098-FBF5-48B2-A2E6-7B3EB4D5DC29
F96CA438-8364-4986-954E-6D50D0DC58DF
53908358-CCA4-44FB-A077-B45394254796
35666EDC-95F9-44FF-BC43-9A033B02FC8A
A862DD8A-2EED-4B32-85F5-8E6F060D4EAD
CE241872-6BCB-4FBB-9E96-793C2D69A81A
F5F27EAA-5F67-4F33-AD68-74829FB5EF71
A4236A40-08C1-41A4-B5F6-7A6FE04BC769
A7F3B9F6-F29C-4BB4-B696-CF75FECBCD56
3FFAA6D4-E626-4F1B-AC41-696C7AFC799A
EC988658-A910-473A-9CE6-54318800AB3A
532AAA9F-F478-45D0-A123-3C2265A873D8
AED8D820-577C-424E-A628-050A4686C4A2
E71E7886-D293-4C9D-98FF-D8087C4BA542
FAE7C2F3-3DEA-47F1-9D8F-B23E2553AD58
D7967C0D-BC0F-43C5-B4B1-3F0C862056D1
F356F27A-51FD-4D4C-9AB4-7337EE66775E
F30CF677-B6EC-44EA-9724-C3DB2AF84DD3
038DAF3A-62A6-433E-9B3E-DBDDD0D66635
6E83C5F0-EB87-46C4-9CEC-19E678D685E0
7C2DE7EA-8066-421C-9869-B1D28C9866D6
7D1324A1-5A13-44C8-B44C-E213A0740F8F
7F50651E-C149-4568-9161-94B652ECDA20
3377B1B1-DEBF-4A61-8CC8-54A72AEEFAC0
1A399115-C053-481B-8759-5752A27F3B4F
2C22163D-07BA-4509-82A3-610FAAAE7FAA
F908118F-C80C-4041-8C8C-D43744A2A258
9E47657D-3169-4125-946C-E70FB3288FFB
56EA2EED-931A-46EB-BB5E-F7E8CBFA57FD
3BF6FAAE-C312-40D8-B15A-0AB0458D9763
DEC4A41D-952D-4D19-91D4-7493D942934A
3B7B413D-3AC0-4AD9-92F3-971932B67837
3B4954DE-86A0-4CA2-99DA-3B6FCA225C8D
AB76A22A-262C-4CD1-984C-55F203E47ECE
4614DE9B-0839-4992-B89B-F06B3A93611F
B29D2E4D-9209-4EF1-AA55-9DF70BF727FE
0mXQZaOVr7Tf$n6oIcHifF
0I6NmBtwTC6RFcqQcbbcEh
0MFlxN6vfEZBQQP9VvN2Cg
0OBAL_38D8pAHwXHJmPjwo
2RjiEtf191gPXHIyomxGKc
1R8r3Vpbv8BvhF2ye6nEJN
3eOl_mPvD3GArUOj6ASFdZ
38WId7FqL6wA$$wiOFxU2_
1C5L5H9a9FeQiAWDAwzVg9
2uIavcb$jFeh7_AoC1SHSC
2FP2WjImjATRUO9KfKfPtl
1clQaoh1H0kBe2OmD9LXg$
1Pwmg9An1A8gP2xiG4cuCU
0qXt6WBeHC99RKPu3sCz02
2fjGlL3hf0SgnZ$7I4Lte_
3JrXunJDXC28mAcYUAgfjE
1E4Le5BGj9ThWHzoJyEPn1
1c5aWz1_DE9v2rfKnGH0uo
0EQe_59yXByPy7Xn696DI0
00mdcruUP7Rh27gNTxToK_
2tJ_aXkov4_Og3zQgiOX2D
0SRBxqBr50MB0gjQeYt_f2
0l$and4i98ZeBRcMOuyfTm
0ZFdve7ZDAe9VTvjI5puvk
0HJjBhorT3qx9Ipk11$DmV
2_dTjcjDn5pelB$gcZfnvf
28Tu$T_2DBaAQAiS5BsUDM
2j87HII2n4of1mYjvacXRj
1DZtCJq6v5v862NYzFWVv8
01bpkkS6P70h$9I6rsDo0S
2EeitIPc91peOyQjY430FR
0$hrEtwQjDN95g8qJ7w2cg
2qpivuG0rCUAyzEqXblfqk
0T3dkM9yD3Y8RHD9ifUMdt
0JozEfr5118ARkGB2jFqkZ
09S2ra0I59tRXHhQceHy8Y
0dVTkR3tnCs9bYeTM9iuQv
2UJwWmDpvBIg$8akrHpy07
1DNrh0vZP2Mh1UdzoVBTvg
3TwERglUj988O1yhthG2iD
1r55rrhvrBzBup9MXi58$r
1oGzzMz4PBh8qV_lsFNUcw
1AW_vTR4D2y9u_HmIWlkBX
2GgzjJLJjF2hbt6MzsVCqh
3vEfLmUjP6Nh0BvsHHzRJs
0JKPSXarrD_hN4S065OvYL
2v4WQYrUP7mxl6Cpq2YPlI
0U3OGqN917E98U18UH1pjx
2KM4zTHxvBK9S0HqH0VFHj
3Xa_MzW_rCOvAe0O3yjJn$
3eeYsUsrXBOB4YQfqWBnXI
2zHxVEFV153eUChjN4O03p
30E5aDoof1euZroiEZ0KVU
2jLqjidlfBP8yvvzLCDgzD
2GB5Afx7DCOAbiRYLkd_PY
0lEe0T4uv7dPIEl$H0XVka
2IbW8IFOX2o9KODxGXMrW7
0RvlQ$4d14vBDSLW1ykSFd
2wHNbCn4v0MQmnau4Xnge1
1IrJ76G0zBj9_t3iEZCVNB
0lMjBHq_rF29STdZ_Cb9pW
3T3q6mXWH6OftxbVYv0cSM
1eZOIVbrLFNRRPU7h4QLyS
0bP3uEEkr8JRC2YnnrodhS
0k6vlU4kL2VPBE8UXo4zkA
1JRPuzxbj3nfloulbb5HnV
0qowYHzLT1fA5olOLOmJhS
16kIkW0Vr32OZZyZPDix0a
2zigntw0TF2x0nsEYPUwFy
1vIzfT2lT5iPhl5n04gqOm
3IZgYma2P5D9DFnz5uRbRr
1As$EqIz16wBEOo7uLPxha
1ih$CvJh112vrL8pjK6kyK
3uq_83E0TBhQ4GCm1fAfU2
2ESs_T93jEtAcd8cmhPONz
10EUVvJ4bBdR756cjILkLu
2ALyVi5jzBTxFzREA4hy38
2JF3u3iPP6FuakCMefEl3U
08i$g_rADE7ABnAWj1iYGC
0cup2v$wv8nQv_swnjKKm5
1oqvgXg$16tgt4_K5mVBEZ
23Wz2feaP7px8eztUk9kUJ
0DGox4liv029ElNwoTJObK
1RUB1e3vv3SgZFzULcSx$3
116zduRIz48uXH4DkzDEXY
0ZZ2VUUV13Rw3DK3lR6unm
0JiGduExXAPxsbY1QUsFSU
2gGvFTeQr3wRS3STTZ5c$u
0dvg7wSkbCUQ_fdJt7IBip
0bnedzB1920xFW94OMfCI_
2rTAHRM714iOG5FROlxgI8
1kUgAPdxn75gpQClPAUVZT
2sY_rbaS58yvjF0Psxp4EG
3zWH9ZAunDePaTfO$P_cUH
2hZ6uD3d14ceOOTZ9EXKdE
13Qolq5$n1UgMBaNE_0ifo
3im5M4IObEP9fbM2ciHuEN
3NW$I87cLAD82pbMfka93b
3lxWfl9ZX6b81Fql2D7dzx
0HhMA1jmH4wuBkMJD$Mzkb
08eWLqOIz3vxPr39nCP4Hi
3w$Yr2FJ944RMpwRHdpRyB
3ewnlyevXB6OtwRrEsBw4V
04ixR_5CH6BBaZvjCsLb_7
2zDoCPXtv0Zwbz2sFieWHu
0dkJ$NrGPFnRC66ZsrUvDZ
0MVvdsV6nCahcUAWetBI_W
04uwnVSGr3iQc9a23yXFCn
1aVn14vgjDFAbI_O$7uZK2
1qXtIlPr517Qq6zHr_XPPV
1zh_3wPsX2QRcR5ac7FzFe
03t$kJo1HCjg$0d7$76X7L
3Bb5Rv62PCOwWTQmym8oty
3sBpMWZED8twcw7NSSv4vc
0yNGnDmrb2CRgqj2MPJS38
12jah$OSrFGOFI6vO$zcWq
102aOPnHXEjv6aaz0Ir9eR
3an5xiZ7n8Ev$z7LfQ9QzR
0lwL2BzH99jxTGsNweyhYa
1N282Gzg110APcchSXUE6o
1IacR$JnD1fQ1G65gJRk_0
3xsIEfHbz7UQvJlO4rHx0E
3EgMePEyH8QPMSg9VNOO4O
30wfwPv4zFl9o3y420HeVb
2HuFLonjTA1OM2eV_oxM5y
3_jNPVoEH4f9R5S10IOBH1
1pXGpDRyPAPAX4ehnizOwA
3jOBWxkjf6IwKD681_$OgU
1swMFuUc1CggJ7D3EyT1MZ
2Sw3PkOnXE3OHxKr8YMmee
0a4B1Ox49EUQKcvFv2PzEH
2zw9avwtD8ewXVTg5m_73G
3bniaA0bz6PwBvgWWg2e76
3FX9DRmBD1QuA5vW2J6Bls
1nmHrBxYHDRw$Uf$XjZ9to
3F8aitiA95LgRl1A1kRIBz
1qYRsUlTz7xvufg6JZY_Gw
1c9Au_Le18NggO_pX5pU4o
324IrDumT5nxGg1Eb7bSWa
0beRrWxxjA8vbUMevNZDFc
17oKW9XF97quLGFPgJI$jR
1XXfw99qr6DQIPxnv$dlFN
375_38aVj3wh23tTslTA$M
2yVbvSRQXD6vEMjLJLzv4q
1PRy8I1bH7sPSPta0kiRAG
3_$1vIL_zBsO67TiKqqWB7
142MNEZJz3zxocR2WnOAGJ
028G$$3K53su5m1lLx9g9v
2$6TSZR4155vtMJ6JApbPJ
0rMf4nXXL7YRxpUIdjTup9
3HLzNu149F4RQnrK7MTeSf
01gz5BA9j9AO7KyQ8$ejKR
1Pa8igRu16q8tBbiVZen7J
3aqqLH17b4ixf5xhgV0gmt
2gZQ1DqofBkATl3Y420MrF
2F4G8c8ALFYAX1jeqIns1l
2AHGRuadT0svLsjjddrdns
2u58rneC56PerbZykn68HU
3nksYkpYr7IQj3uNUffjhN
2uJyvTzCbD0w1U73wsvJm2
3iSYr2T2rElwP9WjFrcMAN
2Ds6kAGwb2aQD3c9eqKlog
12_N_s5gXB5PhpKRBjd06M
0IaQrR80T7uwXQZgtQ1dpA
0b2dyM0810Pw9AdcfmUvIe
2OFO79FovBUPGp3d95Y17z
0TSzlfJBf52xQtJtWRI9Lw
3TYijr9cr279ye0$jbj7uN
2VXwEtngPEIQpzOP0dl5EP
01zIYu4s1F0eI_cyuIUplQ
2m1vc9fjf5phoLxKjjz2xZ
0XdFIw2Cf1Bhzxq$zoWnJO
3AmGynGPHAcQY7iRvWdzww
21ieGKqPf1huLGEk2eXXoW
3yPa_S9ZnF3fr30DfuvK0x
2ZHk1EttT7VfF99yWxQqS6
1WULZobO9389fAtR4A3bND
0gOrAuFxn7RfRSQ568Fo5Q
0KHYfnVBb1ivLSuB98RzsI
10Q_Z_88H5QeBFsJ3e9XMm
1Wm1FJKoH9VhJ79aOGc18r
1FDvTpwzP5pwD28dg8k3wW
0a1WFhgIX0veEbzTJzoXnx
0XrBCNVif8yAa3e0zytxrI
0_F5rj8j56KwzznHwIuQ0M
03X2PbTnzCYeZalXlUOHVh
1ZBbXilvn7sPIsqItvBSrK
26jFTlI4zCWecVxakgfu1j
3lDvyS0qn9686$qw9ZU4OC
1dQom76HH7DBF_MyfcNZbb
1Nxfmw8jD1jfNlAH0HbFhk
2afd9zfF93WACQ5y7NgB$E
3y41P6ZED2lRGQHjpA$9GC
2tOER5$d18lvnTapiFlGFC
1ZWaH8BAn1$vZ9u4WHOJ9Q
3MLXrqtQTAhh39kwjAXXj2
2Jke6l5an1phvw6rEneq6N
3sBPbWSqHExfZ4Gp1g3TOz
11kBlAf5b6DRrThYnXpKBN
1cTCYsFjnDjPOWkWswpXZ3
1U9SmTzCzCnuNZieXLcIGn
0jeGbTuof27B2gNKaq$hj6
3SUt$IdZP4QgqVVDrkNLZs
3WPvCRDpD6j9RcbUBDyqbZ
1$ZMTthBL9DOWeD5T6iX3K
1I76jfZEH75AHCP1PvsFW1
3VD0lFyf90jOWceDMcJwPa
2UKzEvZm9A8v2Idr6rbm_6
3LFakhqITAKhZQ1P2EztP5
1l6FQqoN179htQ0je0UFUi
3fAVQS8qf7NvxOX3gJXcG1
3E_Npwxc57bPpS_E3m_8gs
1k1YrwcCX2bRiMyj6g71Ok
33$uanWmH7d8C0pjCzQZB$
2weSoRHXP9CfOAbfUbwUwP
318n0EqLT3aRa4RQzznjIw
0oWv86Z_f5ux0ybJf3BJqR
1zAZX30o94uuC94Sut4fsr
3Bs$EqFBP7jh0EkjddecCO
0Ctz5paRbEt9yyQukGHVZp
350U2O_$L8igBcUpwqrTmf
3vRAGuWsH9XfLERL3Gt5ZV
1Ja8DOpAH4_w1tj5EK9KUM
0rPcxSbVb4$xn3cWCx0loA
2eOjsABkrBCeNrZcy63Kwj
3E91XoQyjFkvwMUJmjQQWQ
3rydwgNsTFCwreT8AVjUzn
2a8sf02C51fBNsUc$WIyTf
2dyxdsyfnBjBQMptN_oyrM
0$_gRKvYPF6wn1QMnw$7cQ
3ic8POgH17EfpcL3680Aiw
1JAggVz7X5qA4ZF29bg7FO
2ksDWWLtn2JgOe1Gf6XiIY
3d7dY6qfDCdPZ$s0XyIwL2
3wvyBpFUf7yPsFiZubKwrO
3NbdmDl0z3nRInFmo685RH
3pLl9wKVrDJ9gqSpVkPdTU
3p3FPtjkn4wfSamzig_4tJ
03ZQywOgP3Ffi_sztGrcOr
1kWyNmwuT6n9pi6UPureNW
1yBUVgW6P279XfiTACc6RM
1z4oIXMXD4oBHCuXEWT0_F
1$K6KUmKb5Q95XbBPIxDeW
0pTx6nthzAOOp8LASgxlh0
0QEP4Lm5D86uTPLrAYVpjF
0i8XOz1xf52OAZOG_ghd_g
3v216Fo0n0GOoCr3T4eg9O
2UHsLzCMb19PHivm_pA8$x
1MwYxjanf6wxjUz_ZB_bVz
0xzlgkmn90sB5Q2h15ZPTZ
3UnAGTbIrD6P7KT9FPGfDA
0xUq4zEi1AsPBpbnaojdWt
0xILJUXg1CefdQEs$A8boD
2hTg8g9YnCqPXCLV83v7xE
165DwR23b9ahYRy6iwas4V
2odIvDaWbEyQfLdVSBzoV_

At a minimum, ifcc should be able to:

  • Convert one test file into the other with no differences
  • Run the same test file through two passes of ifcc – once to compress, once to decompress – and return the original file unchanged.
.PHONY: test1pass
test1pass: $(bindir)/ifcc $(exdir)/uguids.txt $(exdir)/cguids.txt
	$(bindir)/ifcc -c -i ex/uguids.txt | diff -q - ex/cguids.txt;\
	$(bindir)/ifcc -x -i ex/cguids.txt | diff -q - ex/uguids.txt



.PHONY: test2pass
test2pass: 
	./bin/ifcc -c -i ex/uguids.txt | ./bin/ifcc -x | diff -q - ex/uguids.txt;\
	./bin/ifcc -x -i ex/cguids.txt | ./bin/ifcc -c | diff -q - ex/cguids.txt


.PHONY: check
check: test1pass test2pass

Hygiene

For simplicity, I’m providing a primitive install-uninstall procedure for *nix-based systems and a few helper targets.

prefix=/usr/local
.PHONY: install
install:
	cp $(lib) $(prefix)/lib
	cp $(exe) $(prefix)/bin

.PHONY: uninstall
uninstall:
	rm $(prefix)/lib/libifcidc.so
	rm $(prefix)/bin/ifcc

.PHONY: leaks
leaks:
	valgrind --track-origins=yes ./$(bindir)/ifcc -c -i $(exdir)/uguids.txt -o /dev/null
	valgrind --track-origins=yes ./$(bindir)/ifcc -x -i $(exdir)/cguids.txt -o /dev/null

.PHONY: clean
clean:
	rm -rf $(bindir) $(incdir) $(srcdir) $(libdir) $(exdir) Makefile

Development

ifcidc’s sources can be built entirely from this file. Within an org-enabled Emacs on a modern *nix system, load this file in a buffer, then execute M-x org-babel-tangle followed by make.

License

ifcidc has an MIT license.