Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2019 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
16
 
17
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
18
#include "Crashlytics/Shared/FIRCLSByteUtility.h"
19
 
20
#if TARGET_OS_MAC
21
#include <Foundation/Foundation.h>
22
#endif
23
 
24
#include <sys/stat.h>
25
 
26
#include <stdio.h>
27
#include <string.h>
28
 
29
#include <unistd.h>
30
 
31
// uint64_t should only have max 19 chars in base 10, and less in base 16
32
static const size_t FIRCLSUInt64StringBufferLength = 21;
33
static const size_t FIRCLSStringBufferLength = 16;
34
const size_t FIRCLSWriteBufferLength = 1000;
35
 
36
static bool FIRCLSFileInit(FIRCLSFile* file, int fdm, bool appendMode, bool bufferWrites);
37
 
38
static void FIRCLSFileWriteToFileDescriptorOrBuffer(FIRCLSFile* file,
39
                                                    const char* string,
40
                                                    size_t length);
41
static void FIRCLSFileWriteToBuffer(FIRCLSFile* file, const char* string, size_t length);
42
static void FIRCLSFileWriteToFileDescriptor(FIRCLSFile* file, const char* string, size_t length);
43
 
44
short FIRCLSFilePrepareUInt64(char* buffer, uint64_t number, bool hex);
45
 
46
static void FIRCLSFileWriteString(FIRCLSFile* file, const char* string);
47
static void FIRCLSFileWriteHexEncodedString(FIRCLSFile* file, const char* string);
48
static void FIRCLSFileWriteBool(FIRCLSFile* file, bool value);
49
 
50
static void FIRCLSFileWriteCollectionStart(FIRCLSFile* file, const char openingChar);
51
static void FIRCLSFileWriteCollectionEnd(FIRCLSFile* file, const char closingChar);
52
static void FIRCLSFileWriteCollectionEntryProlog(FIRCLSFile* file);
53
static void FIRCLSFileWriteCollectionEntryEpilog(FIRCLSFile* file);
54
 
55
#define CLS_FILE_DEBUG_LOGGING 0
56
 
57
#pragma mark - File Structure
58
static bool FIRCLSFileInit(FIRCLSFile* file, int fd, bool appendMode, bool bufferWrites) {
59
  if (!file) {
60
    FIRCLSSDKLog("Error: file is null\n");
61
    return false;
62
  }
63
 
64
  if (fd < 0) {
65
    FIRCLSSDKLog("Error: file descriptor invalid\n");
66
    return false;
67
  }
68
 
69
  memset(file, 0, sizeof(FIRCLSFile));
70
 
71
  file->fd = fd;
72
 
73
  file->bufferWrites = bufferWrites;
74
  if (bufferWrites) {
75
    file->writeBuffer = malloc(FIRCLSWriteBufferLength * sizeof(char));
76
    if (!file->writeBuffer) {
77
      FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileInit");
78
      return false;
79
    }
80
 
81
    file->writeBufferLength = 0;
82
  }
83
 
84
  file->writtenLength = 0;
85
  if (appendMode) {
86
    struct stat fileStats;
87
    fstat(fd, &fileStats);
88
    off_t currentFileSize = fileStats.st_size;
89
    if (currentFileSize > 0) {
90
      file->writtenLength += currentFileSize;
91
    }
92
  }
93
 
94
  return true;
95
}
96
 
97
bool FIRCLSFileInitWithPath(FIRCLSFile* file, const char* path, bool bufferWrites) {
98
  return FIRCLSFileInitWithPathMode(file, path, true, bufferWrites);
99
}
100
 
101
bool FIRCLSFileInitWithPathMode(FIRCLSFile* file,
102
                                const char* path,
103
                                bool appendMode,
104
                                bool bufferWrites) {
105
  if (!file) {
106
    FIRCLSSDKLog("Error: file is null\n");
107
    return false;
108
  }
109
 
110
  int mask = O_WRONLY | O_CREAT;
111
 
112
  if (appendMode) {
113
    mask |= O_APPEND;
114
  } else {
115
    mask |= O_TRUNC;
116
  }
117
 
118
  // make sure to call FIRCLSFileInit no matter what
119
  int fd = -1;
120
  if (path) {
121
#if TARGET_OS_IPHONE
122
    /*
123
     * data-protected non-portable open(2) :
124
     * int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode)
125
     */
126
    fd = open_dprotected_np(path, mask, 4, 0, 0644);
127
#else
128
    fd = open(path, mask, 0644);
129
#endif
130
 
131
    if (fd < 0) {
132
      FIRCLSSDKLog("Error: Unable to open file %s\n", strerror(errno));
133
    }
134
  }
135
 
136
  return FIRCLSFileInit(file, fd, appendMode, bufferWrites);
137
}
138
 
139
bool FIRCLSFileClose(FIRCLSFile* file) {
140
  return FIRCLSFileCloseWithOffset(file, NULL);
141
}
142
 
143
bool FIRCLSFileCloseWithOffset(FIRCLSFile* file, off_t* finalSize) {
144
  if (!FIRCLSIsValidPointer(file)) {
145
    return false;
146
  }
147
 
148
  if (file->bufferWrites && FIRCLSIsValidPointer(file->writeBuffer)) {
149
    if (file->writeBufferLength > 0) {
150
      FIRCLSFileFlushWriteBuffer(file);
151
    }
152
    free(file->writeBuffer);
153
  }
154
 
155
  if (FIRCLSIsValidPointer(finalSize)) {
156
    *finalSize = file->writtenLength;
157
  }
158
 
159
  if (close(file->fd) != 0) {
160
    FIRCLSSDKLog("Error: Unable to close file %s\n", strerror(errno));
161
    return false;
162
  }
163
 
164
  memset(file, 0, sizeof(FIRCLSFile));
165
  file->fd = -1;
166
 
167
  return true;
168
}
169
 
170
bool FIRCLSFileIsOpen(FIRCLSFile* file) {
171
  if (!FIRCLSIsValidPointer(file)) {
172
    return false;
173
  }
174
 
175
  return file->fd > -1;
176
}
177
 
178
#pragma mark - Core Writing API
179
void FIRCLSFileFlushWriteBuffer(FIRCLSFile* file) {
180
  if (!FIRCLSIsValidPointer(file)) {
181
    return;
182
  }
183
 
184
  if (!file->bufferWrites) {
185
    return;
186
  }
187
 
188
  FIRCLSFileWriteToFileDescriptor(file, file->writeBuffer, file->writeBufferLength);
189
  file->writeBufferLength = 0;
190
}
191
 
192
static void FIRCLSFileWriteToFileDescriptorOrBuffer(FIRCLSFile* file,
193
                                                    const char* string,
194
                                                    size_t length) {
195
  if (file->bufferWrites) {
196
    if (file->writeBufferLength + length > FIRCLSWriteBufferLength - 1) {
197
      // fill remaining space in buffer
198
      size_t remainingSpace = FIRCLSWriteBufferLength - file->writeBufferLength - 1;
199
      FIRCLSFileWriteToBuffer(file, string, remainingSpace);
200
      FIRCLSFileFlushWriteBuffer(file);
201
 
202
      // write remainder of string to newly-emptied buffer
203
      size_t remainingLength = length - remainingSpace;
204
      FIRCLSFileWriteToFileDescriptorOrBuffer(file, string + remainingSpace, remainingLength);
205
    } else {
206
      FIRCLSFileWriteToBuffer(file, string, length);
207
    }
208
  } else {
209
    FIRCLSFileWriteToFileDescriptor(file, string, length);
210
  }
211
}
212
 
213
void FIRCLSFileWriteStringUnquoted(FIRCLSFile* file, const char* string) {
214
  size_t length = strlen(string);
215
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, string, length);
216
}
217
 
218
static void FIRCLSFileWriteToFileDescriptor(FIRCLSFile* file, const char* string, size_t length) {
219
  if (!FIRCLSFileWriteWithRetries(file->fd, string, length)) {
220
    return;
221
  }
222
 
223
  file->writtenLength += length;
224
}
225
 
226
// Beware calling this method directly: it will truncate the input string if it's longer
227
// than the remaining space in the buffer. It's safer to call through
228
// FIRCLSFileWriteToFileDescriptorOrBuffer.
229
static void FIRCLSFileWriteToBuffer(FIRCLSFile* file, const char* string, size_t length) {
230
  size_t writeLength = length;
231
  if (file->writeBufferLength + writeLength > FIRCLSWriteBufferLength - 1) {
232
    writeLength = FIRCLSWriteBufferLength - file->writeBufferLength - 1;
233
  }
234
  strncpy(file->writeBuffer + file->writeBufferLength, string, writeLength);
235
  file->writeBufferLength += writeLength;
236
  file->writeBuffer[file->writeBufferLength] = '\0';
237
}
238
 
239
bool FIRCLSFileLoopWithWriteBlock(const void* buffer,
240
                                  size_t length,
241
                                  ssize_t (^writeBlock)(const void* buf, size_t len)) {
242
  for (size_t count = 0; length > 0 && count < CLS_FILE_MAX_WRITE_ATTEMPTS; ++count) {
243
    // try to write all that is left
244
    ssize_t ret = writeBlock(buffer, length);
245
 
246
    if (length > SIZE_MAX) {
247
      // if this happens we can't convert it to a signed version due to overflow
248
      return false;
249
    }
250
    const ssize_t signedLength = (ssize_t)length;
251
 
252
    if (ret >= 0 && ret == signedLength) {
253
      return true;
254
    }
255
 
256
    // Write was unsuccessful (out of space, etc)
257
    if (ret < 0) {
258
      return false;
259
    }
260
 
261
    // We wrote more bytes than we expected, abort
262
    if (ret > signedLength) {
263
      return false;
264
    }
265
 
266
    // wrote a portion of the data, adjust and keep trying
267
    if (ret > 0) {
268
      length -= ret;
269
      buffer += ret;
270
      continue;
271
    }
272
 
273
    // return value is <= 0, which is an error
274
    break;
275
  }
276
 
277
  return false;
278
}
279
 
280
bool FIRCLSFileWriteWithRetries(int fd, const void* buffer, size_t length) {
281
  return FIRCLSFileLoopWithWriteBlock(buffer, length,
282
                                      ^ssize_t(const void* partialBuffer, size_t partialLength) {
283
                                        return write(fd, partialBuffer, partialLength);
284
                                      });
285
}
286
 
287
#pragma mark - Strings
288
 
289
static void FIRCLSFileWriteUnbufferedStringWithSuffix(FIRCLSFile* file,
290
                                                      const char* string,
291
                                                      size_t length,
292
                                                      char suffix) {
293
  char suffixBuffer[2];
294
 
295
  // collaspe the quote + suffix into one single write call, for a small performance win
296
  suffixBuffer[0] = '"';
297
  suffixBuffer[1] = suffix;
298
 
299
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1);
300
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, string, length);
301
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, suffixBuffer, suffix == 0 ? 1 : 2);
302
}
303
 
304
static void FIRCLSFileWriteStringWithSuffix(FIRCLSFile* file,
305
                                            const char* string,
306
                                            size_t length,
307
                                            char suffix) {
308
  // 2 for quotes, 1 for suffix (if present) and 1 more for null character
309
  const size_t maxStringSize = FIRCLSStringBufferLength - (suffix == 0 ? 3 : 4);
310
 
311
  if (length >= maxStringSize) {
312
    FIRCLSFileWriteUnbufferedStringWithSuffix(file, string, length, suffix);
313
    return;
314
  }
315
 
316
  // we are trying to achieve this in one write call
317
  // <"><string contents><"><suffix>
318
 
319
  char buffer[FIRCLSStringBufferLength];
320
 
321
  buffer[0] = '"';
322
 
323
  strncpy(buffer + 1, string, length);
324
 
325
  buffer[length + 1] = '"';
326
  length += 2;
327
 
328
  if (suffix) {
329
    buffer[length] = suffix;
330
    length += 1;
331
  }
332
 
333
  // Always add the terminator. strncpy above would copy the terminator, if we supplied length + 1,
334
  // but since we do this suffix adjustment here, it's easier to just fix it up in both cases.
335
  buffer[length + 1] = 0;
336
 
337
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, length);
338
}
339
 
340
void FIRCLSFileWriteString(FIRCLSFile* file, const char* string) {
341
  if (!string) {
342
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, "null", 4);
343
    return;
344
  }
345
 
346
  FIRCLSFileWriteStringWithSuffix(file, string, strlen(string), 0);
347
}
348
 
349
void FIRCLSFileWriteHexEncodedString(FIRCLSFile* file, const char* string) {
350
  if (!file) {
351
    return;
352
  }
353
 
354
  if (!string) {
355
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, "null", 4);
356
    return;
357
  }
358
 
359
  char buffer[CLS_FILE_HEX_BUFFER];
360
 
361
  memset(buffer, 0, sizeof(buffer));
362
 
363
  size_t length = strlen(string);
364
 
365
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1);
366
 
367
  int bufferIndex = 0;
368
  for (int i = 0; i < length; ++i) {
369
    FIRCLSHexFromByte(string[i], &buffer[bufferIndex]);
370
 
371
    bufferIndex += 2;  // 1 char => 2 hex values at a time
372
 
373
    // we can continue only if we have enough space for two more hex
374
    // characters *and* a terminator. So, we need three total chars
375
    // of space
376
    if (bufferIndex >= CLS_FILE_HEX_BUFFER) {
377
      FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, CLS_FILE_HEX_BUFFER);
378
      bufferIndex = 0;
379
    }
380
  }
381
 
382
  // Copy the remainder, which could even be the entire string, if it
383
  // fit into the buffer completely. Be careful with bounds checking here.
384
  // The string needs to be non-empty, and we have to have copied at least
385
  // one pair of hex characters in.
386
  if (bufferIndex > 0 && length > 0) {
387
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, bufferIndex);
388
  }
389
 
390
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1);
391
}
392
 
393
#pragma mark - Integers
394
void FIRCLSFileWriteUInt64(FIRCLSFile* file, uint64_t number, bool hex) {
395
  char buffer[FIRCLSUInt64StringBufferLength];
396
  short i = FIRCLSFilePrepareUInt64(buffer, number, hex);
397
  char* beginning = &buffer[i];  // Write from a pointer to the begining of the string.
398
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, beginning, strlen(beginning));
399
}
400
 
401
void FIRCLSFileFDWriteUInt64(int fd, uint64_t number, bool hex) {
402
  char buffer[FIRCLSUInt64StringBufferLength];
403
  short i = FIRCLSFilePrepareUInt64(buffer, number, hex);
404
  char* beginning = &buffer[i];  // Write from a pointer to the begining of the string.
405
  FIRCLSFileWriteWithRetries(fd, beginning, strlen(beginning));
406
}
407
 
408
void FIRCLSFileWriteInt64(FIRCLSFile* file, int64_t number) {
409
  if (number < 0) {
410
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, "-", 1);
411
    number *= -1;  // make it positive
412
  }
413
 
414
  FIRCLSFileWriteUInt64(file, number, false);
415
}
416
 
417
void FIRCLSFileFDWriteInt64(int fd, int64_t number) {
418
  if (number < 0) {
419
    FIRCLSFileWriteWithRetries(fd, "-", 1);
420
    number *= -1;  // make it positive
421
  }
422
 
423
  FIRCLSFileFDWriteUInt64(fd, number, false);
424
}
425
 
426
short FIRCLSFilePrepareUInt64(char* buffer, uint64_t number, bool hex) {
427
  uint32_t base = hex ? 16 : 10;
428
 
429
  // zero it out, which will add a terminator
430
  memset(buffer, 0, FIRCLSUInt64StringBufferLength);
431
 
432
  // TODO: look at this closer
433
  // I'm pretty sure there is a bug in this code that
434
  // can result in numbers with leading zeros. Technically,
435
  // those are not valid json.
436
 
437
  // Set current index.
438
  short i = FIRCLSUInt64StringBufferLength - 1;
439
 
440
  // Loop through filling in the chars from the end.
441
  do {
442
    char value = number % base + '0';
443
    if (value > '9') {
444
      value += 'a' - '9' - 1;
445
    }
446
 
447
    buffer[--i] = value;
448
  } while ((number /= base) > 0 && i > 0);
449
 
450
  // returns index pointing to the beginning of the string.
451
  return i;
452
}
453
 
454
void FIRCLSFileWriteBool(FIRCLSFile* file, bool value) {
455
  if (value) {
456
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, "true", 4);
457
  } else {
458
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, "false", 5);
459
  }
460
}
461
 
462
void FIRCLSFileWriteSectionStart(FIRCLSFile* file, const char* name) {
463
  FIRCLSFileWriteHashStart(file);
464
  FIRCLSFileWriteHashKey(file, name);
465
}
466
 
467
void FIRCLSFileWriteSectionEnd(FIRCLSFile* file) {
468
  FIRCLSFileWriteHashEnd(file);
469
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\n", 1);
470
}
471
 
472
void FIRCLSFileWriteCollectionStart(FIRCLSFile* file, const char openingChar) {
473
  char string[2];
474
 
475
  string[0] = ',';
476
  string[1] = openingChar;
477
 
478
  if (file->needComma) {
479
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, string, 2);  // write the seperator + opening char
480
  } else {
481
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, &string[1], 1);  // write only the opening char
482
  }
483
 
484
  file->collectionDepth++;
485
 
486
  file->needComma = false;
487
}
488
 
489
void FIRCLSFileWriteCollectionEnd(FIRCLSFile* file, const char closingChar) {
490
  FIRCLSFileWriteToFileDescriptorOrBuffer(file, &closingChar, 1);
491
 
492
  if (file->collectionDepth <= 0) {
493
    //        FIRCLSSafeLog("Collection depth invariant violated\n");
494
    return;
495
  }
496
 
497
  file->collectionDepth--;
498
 
499
  file->needComma = file->collectionDepth > 0;
500
}
501
 
502
void FIRCLSFileWriteCollectionEntryProlog(FIRCLSFile* file) {
503
  if (file->needComma) {
504
    FIRCLSFileWriteToFileDescriptorOrBuffer(file, ",", 1);
505
  }
506
}
507
 
508
void FIRCLSFileWriteCollectionEntryEpilog(FIRCLSFile* file) {
509
  file->needComma = true;
510
}
511
 
512
void FIRCLSFileWriteHashStart(FIRCLSFile* file) {
513
  FIRCLSFileWriteCollectionStart(file, '{');
514
}
515
 
516
void FIRCLSFileWriteHashEnd(FIRCLSFile* file) {
517
  FIRCLSFileWriteCollectionEnd(file, '}');
518
}
519
 
520
void FIRCLSFileWriteHashKey(FIRCLSFile* file, const char* key) {
521
  FIRCLSFileWriteCollectionEntryProlog(file);
522
 
523
  FIRCLSFileWriteStringWithSuffix(file, key, strlen(key), ':');
524
 
525
  file->needComma = false;
526
}
527
 
528
void FIRCLSFileWriteHashEntryUint64(FIRCLSFile* file, const char* key, uint64_t value) {
529
  // no prolog needed because it comes from the key
530
 
531
  FIRCLSFileWriteHashKey(file, key);
532
  FIRCLSFileWriteUInt64(file, value, false);
533
 
534
  FIRCLSFileWriteCollectionEntryEpilog(file);
535
}
536
 
537
void FIRCLSFileWriteHashEntryInt64(FIRCLSFile* file, const char* key, int64_t value) {
538
  // prolog from key
539
  FIRCLSFileWriteHashKey(file, key);
540
  FIRCLSFileWriteInt64(file, value);
541
 
542
  FIRCLSFileWriteCollectionEntryEpilog(file);
543
}
544
 
545
void FIRCLSFileWriteHashEntryString(FIRCLSFile* file, const char* key, const char* value) {
546
  FIRCLSFileWriteHashKey(file, key);
547
  FIRCLSFileWriteString(file, value);
548
 
549
  FIRCLSFileWriteCollectionEntryEpilog(file);
550
}
551
 
552
void FIRCLSFileWriteHashEntryNSString(FIRCLSFile* file, const char* key, NSString* string) {
553
  FIRCLSFileWriteHashEntryString(file, key, [string UTF8String]);
554
}
555
 
556
void FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(FIRCLSFile* file,
557
                                                      const char* key,
558
                                                      NSString* string) {
559
  if ([string length] > 0) {
560
    FIRCLSFileWriteHashEntryString(file, key, [string UTF8String]);
561
  }
562
}
563
 
564
void FIRCLSFileWriteHashEntryHexEncodedString(FIRCLSFile* file,
565
                                              const char* key,
566
                                              const char* value) {
567
  FIRCLSFileWriteHashKey(file, key);
568
  FIRCLSFileWriteHexEncodedString(file, value);
569
 
570
  FIRCLSFileWriteCollectionEntryEpilog(file);
571
}
572
 
573
void FIRCLSFileWriteHashEntryBoolean(FIRCLSFile* file, const char* key, bool value) {
574
  FIRCLSFileWriteHashKey(file, key);
575
  FIRCLSFileWriteBool(file, value);
576
 
577
  FIRCLSFileWriteCollectionEntryEpilog(file);
578
}
579
 
580
void FIRCLSFileWriteArrayStart(FIRCLSFile* file) {
581
  FIRCLSFileWriteCollectionStart(file, '[');
582
}
583
 
584
void FIRCLSFileWriteArrayEnd(FIRCLSFile* file) {
585
  FIRCLSFileWriteCollectionEnd(file, ']');
586
}
587
 
588
void FIRCLSFileWriteArrayEntryUint64(FIRCLSFile* file, uint64_t value) {
589
  FIRCLSFileWriteCollectionEntryProlog(file);
590
 
591
  FIRCLSFileWriteUInt64(file, value, false);
592
 
593
  FIRCLSFileWriteCollectionEntryEpilog(file);
594
}
595
 
596
void FIRCLSFileWriteArrayEntryString(FIRCLSFile* file, const char* value) {
597
  FIRCLSFileWriteCollectionEntryProlog(file);
598
 
599
  FIRCLSFileWriteString(file, value);
600
 
601
  FIRCLSFileWriteCollectionEntryEpilog(file);
602
}
603
 
604
void FIRCLSFileWriteArrayEntryHexEncodedString(FIRCLSFile* file, const char* value) {
605
  FIRCLSFileWriteCollectionEntryProlog(file);
606
 
607
  FIRCLSFileWriteHexEncodedString(file, value);
608
 
609
  FIRCLSFileWriteCollectionEntryEpilog(file);
610
}
611
 
612
NSArray* FIRCLSFileReadSections(const char* path,
613
                                bool deleteOnFailure,
614
                                NSObject* (^transformer)(id obj)) {
615
  if (!FIRCLSIsValidPointer(path)) {
616
    FIRCLSSDKLogError("Error: input path is invalid\n");
617
    return nil;
618
  }
619
 
620
  NSString* pathString = [NSString stringWithUTF8String:path];
621
  NSString* contents = [NSString stringWithContentsOfFile:pathString
622
                                                 encoding:NSUTF8StringEncoding
623
                                                    error:nil];
624
  NSArray* components = [contents componentsSeparatedByString:@"\n"];
625
 
626
  if (!components) {
627
    if (deleteOnFailure) {
628
      unlink(path);
629
    }
630
 
631
    FIRCLSSDKLog("Unable to read file %s\n", path);
632
    return nil;
633
  }
634
 
635
  NSMutableArray* array = [NSMutableArray array];
636
 
637
  // loop through all the entires, and
638
  for (NSString* component in components) {
639
    NSData* data = [component dataUsingEncoding:NSUTF8StringEncoding];
640
 
641
    id obj = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
642
    if (!obj) {
643
      continue;
644
    }
645
 
646
    if (transformer) {
647
      obj = transformer(obj);
648
    }
649
 
650
    if (!obj) {
651
      continue;
652
    }
653
 
654
    [array addObject:obj];
655
  }
656
 
657
  return array;
658
}
659
 
660
NSString* FIRCLSFileHexEncodeString(const char* string) {
661
  size_t length = strlen(string);
662
  char* encodedBuffer = malloc(length * 2 + 1);
663
 
664
  if (!encodedBuffer) {
665
    FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileHexEncodeString");
666
    return nil;
667
  }
668
 
669
  memset(encodedBuffer, 0, length * 2 + 1);
670
 
671
  int bufferIndex = 0;
672
  for (int i = 0; i < length; ++i) {
673
    FIRCLSHexFromByte(string[i], &encodedBuffer[bufferIndex]);
674
 
675
    bufferIndex += 2;  // 1 char => 2 hex values at a time
676
  }
677
 
678
  NSString* stringObject = [NSString stringWithUTF8String:encodedBuffer];
679
 
680
  free(encodedBuffer);
681
 
682
  return stringObject;
683
}
684
 
685
NSString* FIRCLSFileHexDecodeString(const char* string) {
686
  size_t length = strlen(string);
687
  char* decodedBuffer = malloc(length);  // too long, but safe
688
  if (!decodedBuffer) {
689
    FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileHexDecodeString");
690
    return nil;
691
  }
692
 
693
  memset(decodedBuffer, 0, length);
694
 
695
  for (int i = 0; i < length / 2; ++i) {
696
    size_t index = i * 2;
697
 
698
    uint8_t hiNybble = FIRCLSNybbleFromChar(string[index]);
699
    uint8_t lowNybble = FIRCLSNybbleFromChar(string[index + 1]);
700
 
701
    if (hiNybble == FIRCLSInvalidCharNybble || lowNybble == FIRCLSInvalidCharNybble) {
702
      // char is invalid, abort loop
703
      break;
704
    }
705
 
706
    decodedBuffer[i] = (hiNybble << 4) | lowNybble;
707
  }
708
 
709
  NSString* strObject = [NSString stringWithUTF8String:decodedBuffer];
710
 
711
  free(decodedBuffer);
712
 
713
  return strObject;
714
}