javaextensions/pim/framework/src.s60/pimbase64converter.cpp
branchRCL_3
changeset 19 04becd199f91
equal deleted inserted replaced
16:f5050f1da672 19:04becd199f91
       
     1 /*
       
     2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Base64 coder and decoder.
       
    15  *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include "pimbase64converter.h"
       
    21 #include "pimcommon.h"
       
    22 #include "pimpanics.h"
       
    23 #include "logger.h"
       
    24 
       
    25 // CONSTANTS
       
    26 
       
    27 /**
       
    28  * Six least significant bits.
       
    29  */
       
    30 const TUint8 KSixLSBits = 63; // 00111111
       
    31 
       
    32 /**
       
    33  * Eight least significant bits.
       
    34  */
       
    35 const TUint8 KEightLSBits = 255; // 11111111
       
    36 
       
    37 /**
       
    38  * Pad byte.
       
    39  */
       
    40 const TUint8 KPadLetter = '=';
       
    41 
       
    42 /**
       
    43  * A non-base64 letter.
       
    44  * Can be used to denote an erroneous situation.
       
    45  */
       
    46 const TUint8 KNonBase64Letter = 255;
       
    47 
       
    48 /**
       
    49  * Byte array granularity. Byte arrays are usually quite large.
       
    50  */
       
    51 const TInt KByteArrayGranularity = 24;
       
    52 
       
    53 #if 0 // Disabled to satisfy TCK, which expects no newlines
       
    54 /**
       
    55  * Recommended number of encoded characters per line.
       
    56  */
       
    57 const TInt KNumEncodedCharsPerLine = 76;
       
    58 #endif
       
    59 
       
    60 /**
       
    61  * Base64 alphabet from RFC 2045.
       
    62  * 64 characters, indexed from 0 to 63. The pad byte is not included
       
    63  * in the array.
       
    64  *
       
    65  * <pre>
       
    66  *                   Table 1: The Base64 Alphabet
       
    67  *
       
    68  *    Value Encoding  Value Encoding  Value Encoding  Value Encoding
       
    69  *        0 A            17 R            34 i            51 z
       
    70  *        1 B            18 S            35 j            52 0
       
    71  *        2 C            19 T            36 k            53 1
       
    72  *        3 D            20 U            37 l            54 2
       
    73  *        4 E            21 V            38 m            55 3
       
    74  *        5 F            22 W            39 n            56 4
       
    75  *        6 G            23 X            40 o            57 5
       
    76  *        7 H            24 Y            41 p            58 6
       
    77  *        8 I            25 Z            42 q            59 7
       
    78  *        9 J            26 a            43 r            60 8
       
    79  *       10 K            27 b            44 s            61 9
       
    80  *       11 L            28 c            45 t            62 +
       
    81  *       12 M            29 d            46 u            63 /
       
    82  *       13 N            30 e            47 v
       
    83  *       14 O            31 f            48 w         (pad) =
       
    84  *       15 P            32 g            49 x
       
    85  *       16 Q            33 h            50 y
       
    86  *
       
    87  * </pre>
       
    88  */
       
    89 const TUint8 KBase64Alphabet[64] =
       
    90 {
       
    91     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
       
    92     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
       
    93 
       
    94     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
       
    95     'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
       
    96 
       
    97     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
       
    98 };
       
    99 
       
   100 CPIMByteArray* PIMBase64Converter::EncodeBase64L(const CPIMByteArray& aSource)
       
   101 {
       
   102     JELOG2(EPim);
       
   103     CPIMByteArray* targetBytes =
       
   104         new(ELeave) CPIMByteArray(KByteArrayGranularity);
       
   105     CleanupStack::PushL(targetBytes);
       
   106 
       
   107     // Loop through the source array.
       
   108     //
       
   109     // Read an 8 bit byte on each iteration. Insert the byte to current
       
   110     // quantum, shifting it's contents 8 bits left before insertion.
       
   111     // When three bytes are read, append the bytes as four base64-encoded
       
   112     // bytes to the target array. Then reset the quantum and start reading
       
   113     // another three bytes.
       
   114     //
       
   115     // If less than three bytes could be read from the source array,
       
   116     // Convert a partial quantum. If zero bytes were read, the encoding
       
   117     // stops. If one or two bytes were read, pad bytes ('=') are appended
       
   118     // after conversion to make it full four base64 bytes and encoding stops.
       
   119 
       
   120     TUint32 quantum = 0;
       
   121     TInt numBytesInQuantum = 0;
       
   122     const TInt numTotalSourceBytes = aSource.Count();
       
   123     TInt targetByteCount = 0;
       
   124 
       
   125     for (TInt i = 0; i < numTotalSourceBytes; i++)
       
   126     {
       
   127         TUint8 sourceByte = aSource[i]; // 8 raw, non-encoded bits
       
   128         quantum <<= 8;
       
   129         quantum |= sourceByte;
       
   130         numBytesInQuantum++;
       
   131 
       
   132         if (numBytesInQuantum == 3)
       
   133         {
       
   134             // 24 bits in the quantum
       
   135             EncodeQuantumL(quantum, *targetBytes, 3);
       
   136             quantum = 0;
       
   137             numBytesInQuantum = 0;
       
   138             targetByteCount += 4;
       
   139         }
       
   140 
       
   141 #if 0 // Disabled to satisfy TCK, which expects no newlines
       
   142         // PIM API TCK does not accept newlines in output.
       
   143         if ((targetByteCount> 0) &&
       
   144                 ((targetByteCount % KNumEncodedCharsPerLine) == 0))
       
   145         {
       
   146             targetBytes->AppendL('\n');
       
   147         }
       
   148 #endif
       
   149     }
       
   150 
       
   151     // 0, 8 or 16 bits in the quantum
       
   152     EncodeQuantumL(quantum, *targetBytes, numBytesInQuantum);
       
   153 
       
   154     CleanupStack::Pop(targetBytes);
       
   155     return targetBytes;
       
   156 
       
   157 }
       
   158 
       
   159 CPIMByteArray* PIMBase64Converter::DecodeBase64L(const CPIMByteArray& aSource)
       
   160 {
       
   161     JELOG2(EPim);
       
   162     CPIMByteArray* targetBytes =
       
   163         new(ELeave) CPIMByteArray(KByteArrayGranularity);
       
   164     CleanupStack::PushL(targetBytes);
       
   165 
       
   166     // Loop through the source array.
       
   167     //
       
   168     // Read an 8 bit byte on each iteration. If the byte is a pad byte, stop
       
   169     // reading. If the byte is not a base64 letter, skip it. Otherwise,
       
   170     // consider it a base64 letter and convert it to the corresponding 6 bit
       
   171     // value (in an 8 bit byte, of course). Insert the 6 bits to a quantum,
       
   172     // shifting its contents 6 bits left before insertion. When four bytes
       
   173     // are read, append the bytes as three base64-decoded bytes to the target
       
   174     // array. Then reset the quantum and start reading another four bytes.
       
   175     //
       
   176     // If a pad byte was read before full quantum was constructed, a partial
       
   177     // quantum is constructed. Quantum decoding method can also parse an empty
       
   178     // quantum, in which case it does nothing.
       
   179 
       
   180     TUint32 quantum = 0;
       
   181     TInt numBytesInQuantum = 0;
       
   182     const TInt numTotalSourceBytes = aSource.Count();
       
   183 
       
   184     for (TInt i = 0; i < numTotalSourceBytes; i++)
       
   185     {
       
   186         TUint8 sourceBase64Letter = aSource[i];
       
   187 
       
   188         if (sourceBase64Letter == KPadLetter)
       
   189         {
       
   190             break; // End of input
       
   191         }
       
   192 
       
   193         TUint8 sourceByte = Base64LetterToByte(sourceBase64Letter);
       
   194 
       
   195         if (sourceByte == KNonBase64Letter)
       
   196         {
       
   197             continue; // Skip
       
   198         }
       
   199 
       
   200         quantum <<= 6;
       
   201         quantum |= sourceByte;
       
   202         numBytesInQuantum++;
       
   203 
       
   204         if (numBytesInQuantum == 4)
       
   205         {
       
   206             // 24 bits in quantum
       
   207             DecodeQuantumL(quantum, *targetBytes, 4);
       
   208             quantum = 0;
       
   209             numBytesInQuantum = 0;
       
   210         }
       
   211     }
       
   212 
       
   213     // 0, 6, 12 or 18 bits in quantum
       
   214     DecodeQuantumL(quantum, *targetBytes, numBytesInQuantum);
       
   215 
       
   216     CleanupStack::Pop(targetBytes);
       
   217     return targetBytes;
       
   218 }
       
   219 
       
   220 void PIMBase64Converter::EncodeQuantumL(const TUint32& aQuantum,
       
   221                                         CPIMByteArray& aTargetArray, const TInt& aNumSourceBytes)
       
   222 {
       
   223     JELOG2(EPim);
       
   224     TInt numTargetBytes = 0;
       
   225     TInt padShift = 0;
       
   226 
       
   227     switch (aNumSourceBytes)
       
   228     {
       
   229     case 3: // 24 bits
       
   230     {
       
   231         numTargetBytes = 4; // 6 + 6 + 6 + 6
       
   232         break;
       
   233     }
       
   234     case 2: // 16 bits
       
   235     {
       
   236         numTargetBytes = 3; // 6 + 6 + 4
       
   237         padShift = 2;
       
   238         break;
       
   239     }
       
   240     case 1: // 8 bits
       
   241     {
       
   242         numTargetBytes = 2; // 6 + 2
       
   243         padShift = 4;
       
   244         break;
       
   245     }
       
   246     case 0: // 0 bits
       
   247     {
       
   248         return; // Done.
       
   249     }
       
   250     default:
       
   251     {
       
   252         User::Leave(KErrArgument);
       
   253     }
       
   254     }
       
   255 
       
   256     TInt numPadBytes = 4 - numTargetBytes;
       
   257 
       
   258     while (numTargetBytes > 0)
       
   259     {
       
   260         numTargetBytes--;
       
   261         TUint32 tempQuantum = aQuantum;
       
   262         TInt shift = 6 * numTargetBytes;
       
   263         tempQuantum <<= padShift;
       
   264         tempQuantum >>= shift;
       
   265         tempQuantum &= KSixLSBits;
       
   266 
       
   267         TUint8 byte = static_cast<TUint8>(tempQuantum);
       
   268         TUint8 base64Letter = ByteToBase64Letter(byte);
       
   269         aTargetArray.AppendL(base64Letter);
       
   270     }
       
   271 
       
   272     while (numPadBytes > 0)
       
   273     {
       
   274         aTargetArray.AppendL(KPadLetter);
       
   275         numPadBytes--;
       
   276     }
       
   277 }
       
   278 
       
   279 void PIMBase64Converter::DecodeQuantumL(const TUint32& aQuantum,
       
   280                                         CPIMByteArray& aTargetArray, const TInt& aNumSourceBytes)
       
   281 {
       
   282     JELOG2(EPim);
       
   283     TInt numTargetBytes = 0;
       
   284     TInt padShift = 0;
       
   285 
       
   286     switch (aNumSourceBytes)
       
   287     {
       
   288     case 4: // 24 bits
       
   289     {
       
   290         numTargetBytes = 3; // 8 + 8 + 8 bits
       
   291         break;
       
   292     }
       
   293     case 3: // 18 bits
       
   294     {
       
   295         numTargetBytes = 2; // 8 + 8 (+ 2) bits
       
   296         padShift = 2;
       
   297         break;
       
   298     }
       
   299     case 2: // 12 bits
       
   300     {
       
   301         numTargetBytes = 1; // 8 (+ 4) bits
       
   302         padShift = 4;
       
   303         break;
       
   304     }
       
   305     case 1: // 6 bits
       
   306     {
       
   307         // This should never happen
       
   308         __ASSERT_DEBUG(EFalse, User::Panic(KPIMPanicCategory,
       
   309                                            EPIMPanicBase64Error));
       
   310         break;
       
   311     }
       
   312     case 0: // 0 bits
       
   313     {
       
   314         return; // Done.
       
   315     }
       
   316     default:
       
   317     {
       
   318         User::Leave(KErrArgument);
       
   319     }
       
   320     }
       
   321 
       
   322     while (numTargetBytes > 0)
       
   323     {
       
   324         numTargetBytes--;
       
   325         TUint32 tempQuantum = aQuantum;
       
   326         tempQuantum >>= (8 * numTargetBytes + padShift);
       
   327         tempQuantum &= KEightLSBits;
       
   328         TUint8 byte = static_cast<TUint8>(tempQuantum);
       
   329         aTargetArray.AppendL(byte);
       
   330     }
       
   331 }
       
   332 
       
   333 inline TUint8 PIMBase64Converter::ByteToBase64Letter(const TUint8& aByte)
       
   334 {
       
   335     JELOG2(EPim);
       
   336     __ASSERT_DEBUG(aByte < 64, User::Panic(KPIMPanicCategory,
       
   337                                            EPIMPanicBase64Error));
       
   338 
       
   339     return KBase64Alphabet[aByte];
       
   340 }
       
   341 
       
   342 TUint8 PIMBase64Converter::Base64LetterToByte(const TUint8& aLetter)
       
   343 {
       
   344     JELOG2(EPim);
       
   345     const TUint8 KUCAlphaBase = 0; // first upper case base64 letter
       
   346     const TUint8 KLCAlphaBase = 26; // first lower case base64 letter
       
   347     const TUint8 KNumBase = 52; // first numerical letter
       
   348     const TUint8 KPlus = 62; // '+' character
       
   349     const TUint8 KSlash = 63; // '/' character
       
   350 
       
   351     TUint8 retVal = 0;
       
   352 
       
   353     if ((aLetter >= 'A') && (aLetter <= 'Z'))
       
   354     {
       
   355         retVal = static_cast<TUint8>(aLetter - 'A' + KUCAlphaBase);
       
   356     }
       
   357     else if ((aLetter >= 'a') && (aLetter <= 'z'))
       
   358     {
       
   359         retVal = static_cast<TUint8>(aLetter - 'a' + KLCAlphaBase);
       
   360     }
       
   361     else if ((aLetter >= '0') && (aLetter <= '9'))
       
   362     {
       
   363         retVal = static_cast<TUint8>(aLetter - '0' + KNumBase);
       
   364     }
       
   365     else if (aLetter == '+')
       
   366     {
       
   367         retVal = KPlus;
       
   368     }
       
   369     else if (aLetter == '/')
       
   370     {
       
   371         retVal = KSlash;
       
   372     }
       
   373     else
       
   374     {
       
   375         retVal = KNonBase64Letter;
       
   376     }
       
   377 
       
   378     __ASSERT_DEBUG((retVal < 64) || (retVal == KNonBase64Letter), User::Panic(
       
   379                        KPIMPanicCategory, EPIMPanicBase64Error));
       
   380 
       
   381     return retVal;
       
   382 }
       
   383 
       
   384 //  End of File