Back to index

nordugrid-arc-nox  1.1.0~rc6
CheckSum.cpp
Go to the documentation of this file.
00001 // -*- indent-tabs-mode: nil -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 #include <config.h>
00005 #endif
00006 
00007 #include <cctype>
00008 
00009 #include <sys/types.h>
00010 
00011 #include <arc/data/CheckSum.h>
00012 
00013 #ifdef WIN32
00014 typedef unsigned char u_char;
00015 typedef unsigned int u_int;
00016 #endif
00017 
00018 // ----------------------------------------------------------------------------
00019 // This is CRC(32bit) implementation as in 'cksum' utility
00020 // ----------------------------------------------------------------------------
00021 
00022 //  g=0x0000000104C11DB7LL;
00023 
00024 static uint32_t gtable[256] = {
00025   0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
00026   0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
00027   0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
00028   0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
00029   0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9,
00030   0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
00031   0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011,
00032   0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
00033   0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
00034   0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
00035   0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81,
00036   0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
00037   0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49,
00038   0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
00039   0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1,
00040   0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
00041   0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE,
00042   0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
00043   0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
00044   0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
00045   0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE,
00046   0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
00047   0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066,
00048   0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
00049   0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E,
00050   0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
00051   0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6,
00052   0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
00053   0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
00054   0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
00055   0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686,
00056   0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
00057   0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637,
00058   0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
00059   0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F,
00060   0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
00061   0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47,
00062   0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
00063   0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
00064   0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
00065   0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7,
00066   0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
00067   0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F,
00068   0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
00069   0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7,
00070   0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
00071   0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F,
00072   0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
00073   0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
00074   0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
00075   0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8,
00076   0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
00077   0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30,
00078   0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
00079   0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088,
00080   0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
00081   0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0,
00082   0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
00083   0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
00084   0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
00085   0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0,
00086   0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
00087   0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668,
00088   0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
00089 };
00090 
00091 namespace Arc {
00092 
00093   CRC32Sum::CRC32Sum(void) {
00094     start();
00095   }
00096 
00097   void CRC32Sum::start(void) {
00098     r = 0;
00099     count = 0;
00100     computed = false;
00101   }
00102 
00103   void CRC32Sum::add(void *buf, unsigned long long int len) {
00104     for (unsigned long long int i = 0; i < len; i++) {
00105       unsigned char c = (r >> 24);
00106       r = ((r << 8) | ((unsigned char*)buf)[i]) ^ gtable[c];
00107     }
00108     count += len;
00109   }
00110 
00111   void CRC32Sum::end(void) {
00112     if (computed)
00113       return;
00114     unsigned long long l = count;
00115     for (; l;) {
00116       unsigned char c = (l & 0xFF);
00117       ((CheckSum*)this)->add(&c, 1);
00118       l >>= 8;
00119     }
00120     uint32_t u = 0;
00121     ((CheckSum*)this)->add(&u, 4);
00122     r = ((~r) & 0xFFFFFFFF);
00123     computed = true;
00124   }
00125 
00126   int CRC32Sum::print(char *buf, int len) const {
00127     if (!computed) {
00128       if (len > 0)
00129         buf[0] = 0;
00130       return 0;
00131     }
00132     return snprintf(buf, len, "cksum: %08x", r);
00133   }
00134 
00135   void CRC32Sum::scan(const char *buf) {
00136     computed = false;
00137     int l;
00138     if (strncasecmp("cksum:", buf, 6) == 0) {
00139       unsigned long long rr; // for compatibilty with bug in 0.4
00140       l = sscanf(buf + 6, "%llx", &rr);
00141       r = rr;
00142     }
00143     else {
00144       int i;
00145       l = 0;
00146       for (i = 0; buf[i]; i++)
00147         if (!isdigit(buf[i]))
00148           break;
00149       if (!(buf[i]))
00150         l = sscanf(buf, "%u", &r);
00151       else {
00152         for (i = 0; buf[i]; i++)
00153           if (!isxdigit(buf[i]))
00154             break;
00155         if (!(buf[i])) {
00156           unsigned long long rr;
00157           l = sscanf(buf, "%llx", &rr);
00158           r = rr;
00159         }
00160       }
00161     }
00162     if (l != 1)
00163       return;
00164     computed = true;
00165     return;
00166   }
00167 
00168 
00169   // ----------------------------------------------------------------------------
00170   // This is MD5 implementation for LOW-ENDIAN machines derived directly from RFC
00171   // ----------------------------------------------------------------------------
00172 
00173 #define F(X, Y, Z) (((X)&(Y)) | ((~(X)) & (Z)))
00174 #define G(X, Y, Z) (((X)&(Z)) | ((Y)&(~(Z))))
00175 #define H(X, Y, Z) ((X) ^ (Y) ^ (Z))
00176 #define I(X, Y, Z) ((Y) ^ ((X) | (~(Z))))
00177 
00178 #define OP1(a, b, c, d, k, s, i) { \
00179     uint32_t t = ((a) + F(b, c, d) + X[k] + T[i - 1]); \
00180     (a) = (b) + (((t) << (s)) | ((t) >> (32 - s))); \
00181 }
00182 
00183 #define OP2(a, b, c, d, k, s, i) { \
00184     uint32_t t = ((a) + G(b, c, d) + X[k] + T[i - 1]); \
00185     (a) = (b) + (((t) << (s)) | ((t) >> (32 - s))); \
00186 }
00187 
00188 #define OP3(a, b, c, d, k, s, i) { \
00189     uint32_t t = ((a) + H(b, c, d) + X[k] + T[i - 1]); \
00190     (a) = (b) + (((t) << (s)) | ((t) >> (32 - s))); \
00191 }
00192 
00193 #define OP4(a, b, c, d, k, s, i) { \
00194     uint32_t t = ((a) + I(b, c, d) + X[k] + T[i - 1]); \
00195     (a) = (b) + (((t) << (s)) | ((t) >> (32 - s))); \
00196 }
00197 
00198 #define A_INIT (0x67452301)
00199 #define B_INIT (0xefcdab89)
00200 #define C_INIT (0x98badcfe)
00201 #define D_INIT (0x10325476)
00202 
00203   static uint32_t T[64] = {
00204     3614090360U, 3905402710U, 606105819U, 3250441966U,
00205     4118548399U, 1200080426U, 2821735955U, 4249261313U,
00206     1770035416U, 2336552879U, 4294925233U, 2304563134U,
00207     1804603682U, 4254626195U, 2792965006U, 1236535329U,
00208     4129170786U, 3225465664U, 643717713U, 3921069994U,
00209     3593408605U, 38016083U, 3634488961U, 3889429448U,
00210     568446438U, 3275163606U, 4107603335U, 1163531501U,
00211     2850285829U, 4243563512U, 1735328473U, 2368359562U,
00212     4294588738U, 2272392833U, 1839030562U, 4259657740U,
00213     2763975236U, 1272893353U, 4139469664U, 3200236656U,
00214     681279174U, 3936430074U, 3572445317U, 76029189U,
00215     3654602809U, 3873151461U, 530742520U, 3299628645U,
00216     4096336452U, 1126891415U, 2878612391U, 4237533241U,
00217     1700485571U, 2399980690U, 4293915773U, 2240044497U,
00218     1873313359U, 4264355552U, 2734768916U, 1309151649U,
00219     4149444226U, 3174756917U, 718787259U, 3951481745U
00220   };
00221 
00222 
00223   MD5Sum::MD5Sum(void) {
00224     // for(u_int i = 1;i<=64;i++) T[i-1]=(uint32_t)(4294967296LL*fabs(sin(i)));
00225     start();
00226   }
00227 
00228   void MD5Sum::start(void) {
00229     A = A_INIT;
00230     B = B_INIT;
00231     C = C_INIT;
00232     D = D_INIT;
00233     count = 0;
00234     Xlen = 0;
00235     computed = false;
00236   }
00237 
00238   void MD5Sum::add(void *buf, unsigned long long int len) {
00239     u_char *buf_ = (u_char*)buf;
00240     for (; len;) {
00241       if (Xlen < 64) { // 16 words = 64 bytes
00242         u_int l = 64 - Xlen;
00243         if (len < l)
00244           l = len;
00245         memcpy(((u_char*)X) + Xlen, buf_, l);
00246         Xlen += l;
00247         count += l;
00248         len -= l;
00249         buf_ += l;
00250       }
00251       if (Xlen < 64)
00252         return;
00253 
00254       uint32_t AA = A;
00255       uint32_t BB = B;
00256       uint32_t CC = C;
00257       uint32_t DD = D;
00258 
00259 
00260       OP1(A, B, C, D, 0, 7, 1);
00261       OP1(D, A, B, C, 1, 12, 2);
00262       OP1(C, D, A, B, 2, 17, 3);
00263       OP1(B, C, D, A, 3, 22, 4);
00264 
00265       OP1(A, B, C, D, 4, 7, 5);
00266       OP1(D, A, B, C, 5, 12, 6);
00267       OP1(C, D, A, B, 6, 17, 7);
00268       OP1(B, C, D, A, 7, 22, 8);
00269 
00270       OP1(A, B, C, D, 8, 7, 9);
00271       OP1(D, A, B, C, 9, 12, 10);
00272       OP1(C, D, A, B, 10, 17, 11);
00273       OP1(B, C, D, A, 11, 22, 12);
00274 
00275       OP1(A, B, C, D, 12, 7, 13);
00276       OP1(D, A, B, C, 13, 12, 14);
00277       OP1(C, D, A, B, 14, 17, 15);
00278       OP1(B, C, D, A, 15, 22, 16);
00279 
00280 
00281       OP2(A, B, C, D, 1, 5, 17);
00282       OP2(D, A, B, C, 6, 9, 18);
00283       OP2(C, D, A, B, 11, 14, 19);
00284       OP2(B, C, D, A, 0, 20, 20);
00285 
00286       OP2(A, B, C, D, 5, 5, 21);
00287       OP2(D, A, B, C, 10, 9, 22);
00288       OP2(C, D, A, B, 15, 14, 23);
00289       OP2(B, C, D, A, 4, 20, 24);
00290 
00291       OP2(A, B, C, D, 9, 5, 25);
00292       OP2(D, A, B, C, 14, 9, 26);
00293       OP2(C, D, A, B, 3, 14, 27);
00294       OP2(B, C, D, A, 8, 20, 28);
00295 
00296       OP2(A, B, C, D, 13, 5, 29);
00297       OP2(D, A, B, C, 2, 9, 30);
00298       OP2(C, D, A, B, 7, 14, 31);
00299       OP2(B, C, D, A, 12, 20, 32);
00300 
00301 
00302       OP3(A, B, C, D, 5, 4, 33);
00303       OP3(D, A, B, C, 8, 11, 34);
00304       OP3(C, D, A, B, 11, 16, 35);
00305       OP3(B, C, D, A, 14, 23, 36);
00306 
00307       OP3(A, B, C, D, 1, 4, 37);
00308       OP3(D, A, B, C, 4, 11, 38);
00309       OP3(C, D, A, B, 7, 16, 39);
00310       OP3(B, C, D, A, 10, 23, 40);
00311 
00312       OP3(A, B, C, D, 13, 4, 41);
00313       OP3(D, A, B, C, 0, 11, 42);
00314       OP3(C, D, A, B, 3, 16, 43);
00315       OP3(B, C, D, A, 6, 23, 44);
00316 
00317       OP3(A, B, C, D, 9, 4, 45);
00318       OP3(D, A, B, C, 12, 11, 46);
00319       OP3(C, D, A, B, 15, 16, 47);
00320       OP3(B, C, D, A, 2, 23, 48);
00321 
00322 
00323       OP4(A, B, C, D, 0, 6, 49);
00324       OP4(D, A, B, C, 7, 10, 50);
00325       OP4(C, D, A, B, 14, 15, 51);
00326       OP4(B, C, D, A, 5, 21, 52);
00327 
00328       OP4(A, B, C, D, 12, 6, 53);
00329       OP4(D, A, B, C, 3, 10, 54);
00330       OP4(C, D, A, B, 10, 15, 55);
00331       OP4(B, C, D, A, 1, 21, 56);
00332 
00333       OP4(A, B, C, D, 8, 6, 57);
00334       OP4(D, A, B, C, 15, 10, 58);
00335       OP4(C, D, A, B, 6, 15, 59);
00336       OP4(B, C, D, A, 13, 21, 60);
00337 
00338       OP4(A, B, C, D, 4, 6, 61);
00339       OP4(D, A, B, C, 11, 10, 62);
00340       OP4(C, D, A, B, 2, 15, 63);
00341       OP4(B, C, D, A, 9, 21, 64);
00342 
00343 
00344       A += AA;
00345       B += BB;
00346       C += CC;
00347       D += DD;
00348       Xlen = 0;
00349     }
00350   }
00351 
00352   void MD5Sum::end(void) {
00353     if (computed)
00354       return;
00355     // pad
00356     uint64_t l = 8 * count; // number of bits
00357     u_char c = 0x80;
00358     add(&c, 1);
00359     c = 0;
00360     for (; Xlen != 56;)
00361       add(&c, 1);
00362     add(&l, 8);
00363     computed = true;
00364   }
00365 
00366   int MD5Sum::print(char *buf, int len) const {
00367     if (!computed) {
00368       if (len > 0)
00369         buf[0] = 0;
00370       return 0;
00371     }
00372     return snprintf(buf, len,
00373                     "md5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
00374                     ((u_char*)&A)[0], ((u_char*)&A)[1], ((u_char*)&A)[2], ((u_char*)&A)[3],
00375                     ((u_char*)&B)[0], ((u_char*)&B)[1], ((u_char*)&B)[2], ((u_char*)&B)[3],
00376                     ((u_char*)&C)[0], ((u_char*)&C)[1], ((u_char*)&C)[2], ((u_char*)&C)[3],
00377                     ((u_char*)&D)[0], ((u_char*)&D)[1], ((u_char*)&D)[2], ((u_char*)&D)[3]
00378                     );
00379   }
00380 
00381   void MD5Sum::scan(const char *buf) {
00382     computed = false;
00383     if (strncasecmp("md5:", buf, 4) != 0)
00384       return;
00385     int l = sscanf(buf + 4,
00386                    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
00387                    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
00388                    ((u_char*)&A) + 0, ((u_char*)&A) + 1, ((u_char*)&A) + 2, ((u_char*)&A) + 3,
00389                    ((u_char*)&B) + 0, ((u_char*)&B) + 1, ((u_char*)&B) + 2, ((u_char*)&B) + 3,
00390                    ((u_char*)&C) + 0, ((u_char*)&C) + 1, ((u_char*)&C) + 2, ((u_char*)&C) + 3,
00391                    ((u_char*)&D) + 0, ((u_char*)&D) + 1, ((u_char*)&D) + 2, ((u_char*)&D) + 3
00392                    );
00393     if (l != 16)
00394       return;
00395     computed = true;
00396     return;
00397   }
00398 
00399   // --------------------------------------------------------------------------
00400   // This is a wrapper for any supported checksum
00401   // --------------------------------------------------------------------------
00402 
00403   CheckSumAny::CheckSumAny(const char *type)
00404     : cs(NULL),
00405       tp(CheckSumAny::none) {
00406     if (!type)
00407       return;
00408     if (strncasecmp("cksum", type, 5) == 0) {
00409       cs = new CRC32Sum;
00410       tp = cksum;
00411       return;
00412     }
00413     if (strncasecmp("md5", type, 3) == 0) {
00414       cs = new MD5Sum;
00415       tp = md5;
00416       return;
00417     }
00418     if(strncasecmp("adler32",type,7) == 0) {
00419       cs=new Adler32Sum; 
00420       tp=adler32; 
00421       return;
00422     }
00423   }
00424 
00425   CheckSumAny::CheckSumAny(type type) {
00426     if (type == cksum) {
00427       cs = new CRC32Sum;
00428       tp = type;
00429       return;
00430     }
00431     if (type == md5) {
00432       cs = new MD5Sum;
00433       tp = type;
00434       return;
00435     }
00436     if (type == adler32) {
00437       cs = new Adler32Sum;
00438       tp = type;
00439       return;
00440     }
00441   }
00442 
00443   CheckSumAny::type CheckSumAny::Type(const char *crc) {
00444     if (!crc)
00445       return none;
00446     if (!crc[0])
00447       return none;
00448     const char *p = strchr(crc, ':');
00449     if (!p) {
00450       p = crc + strlen(crc);
00451       int i;
00452       for (i = 0; crc[i]; i++)
00453         if (!isxdigit(crc[i]))
00454           break;
00455       if (!(crc[i]))
00456         return cksum;
00457     }
00458     if (((p - crc) == 5) && (strncasecmp(crc, "cksum", 5) == 0))
00459       return cksum;
00460     if (((p - crc) == 3) && (strncasecmp(crc, "md5", 3) == 0))
00461       return md5;
00462     if (((p - crc) == 7) && (strncasecmp(crc, "adler32", 7) == 0))
00463       return adler32;
00464     if (((p - crc) == 9) && (strncasecmp(crc, "undefined", 9) == 0))
00465       return undefined;
00466     return unknown;
00467   }
00468 
00469   void CheckSumAny::operator=(const char *type) {
00470     if (cs)
00471       delete cs;
00472     cs = NULL;
00473     tp = none;
00474     if (!type)
00475       return;
00476     if (strncasecmp("cksum", type, 5) == 0) {
00477       cs = new CRC32Sum;
00478       tp = cksum;
00479       return;
00480     }
00481     if (strncasecmp("md5", type, 3) == 0) {
00482       cs = new MD5Sum;
00483       tp = md5;
00484       return;
00485     }
00486     if (strncasecmp("adler32", type, 7) == 0) {
00487       cs = new Adler32Sum;
00488       tp = adler32;
00489       return;
00490     }
00491   }
00492 
00493   bool CheckSumAny::operator==(const char *s) {
00494     if (!cs)
00495       return false;
00496     if (!(*cs))
00497       return false;
00498     if (!s)
00499       return false;
00500     CheckSumAny c(s);
00501     if (!(c.cs))
00502       return false;
00503     c.cs->scan(s);
00504     if (!(*(c.cs)))
00505       return false;
00506     if (c.tp != tp)
00507       return false;
00508     unsigned char *res;
00509     unsigned char *res_;
00510     unsigned int len;
00511     unsigned int len_;
00512     cs->result(res, len);
00513     c.cs->result(res_, len_);
00514     if (len != len_)
00515       return false;
00516     if (memcmp(res, res_, len) != 0)
00517       return false;
00518     return true;
00519   }
00520 
00521   bool CheckSumAny::operator==(const CheckSumAny& c) {
00522     if (!cs)
00523       return false;
00524     if (!(*cs))
00525       return false;
00526     if (!c)
00527       return false;
00528     unsigned char *res;
00529     unsigned char *res_;
00530     unsigned int len;
00531     unsigned int len_;
00532     cs->result(res, len);
00533     c.cs->result(res_, len_);
00534     if (len != len_)
00535       return false;
00536     if (memcmp(res, res_, len) != 0)
00537       return false;
00538     return true;
00539   }
00540 
00541 } // namespace Arc