Совместимость сжатия zlib между Objective- C & Java (с использованием предустановленного словаря) - PullRequest
0 голосов
/ 28 февраля 2020

У меня проблемы с совместимостью zlib. Я пытаюсь внедрить сжатие в мою систему, которая состоит из серверных и телефонных приложений. Бэкэнд написан на Java, а также приложение Android и приложение iOS на Objective- C. Я использую raw deflate (без заголовка и контрольной суммы). Процесс выглядит следующим образом: сжатие в приложении телефона -> распаковка в бэкэнд -> обработка данных -> сжатие результата -> распаковка в приложении телефона. Сжатие в основном используется для сжатия короткого текста, содержащего предсказуемые слова.

Все отлично работает, если не использовать предустановленный словарь, но при добавлении предустановленного словаря возникает проблема при декодировании данных (на бэкэнде). Слова, содержащиеся в словаре, не распакованы должным образом и фактически смешаны вместе.

Пример:

Словарь: # 1 # test # 1234567890 (backend, iOS и Android)
Текст перед сжатием: # test1 # 1 # test2 # abcd # 783478 (iOS)
Текст после распаковки: 567891 # 4567892 # abcd # 783478 (backend)

Вы можете заметить, что вместо "#test" результат будет "56789" , который также является частью словаря.

Кажется, что есть какое-то смещение с декомпрессией при отправке из iOS в бэкэнд, в то время как связь между Android и бэкэндом работает нормально. Я пытался использовать разные словари (которые не вызывали проблем), но степень сжатия была намного ниже по сравнению со словарем, использованным в этом примере, который отлично отвечает потребностям системы. Я не вижу, что может вызвать такую ​​проблему с декомпрессией. Ниже приведен код, используемый для сжатия на стороне iOS и декомпрессии на прокси:

iOS Сжатие

  //this is category for NSData
  (NSData *)zlibDeflate {

    /* Compresssion Levels:
        - Z_NO_COMPRESSION
        - Z_BEST_SPEED
        - Z_BEST_COMPRESSION
        - Z_DEFAULT_COMPRESSION
    */

    if ([self length] == 0) return self;

    z_stream strm;
    bzero(&strm, sizeof(z_stream));

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in = (Bytef *)[self bytes];
    strm.avail_in = (uInt)[self length];

    NSString *dict = @"#1#test#1234567890";
    NSData *dictData = [dict dataUsingEncoding: NSUTF8StringEncoding];
    const Bytef dictBytes = (const Bytef )[dictData bytes];
    int success;

    /* more info https://www.zlib.net/manual.html
     deflateInit2((z_streamp strm
     int  level
     int  method
     int  windowBits: –8..–15 is used for a RAW deflate method without header and trailer in the final data.
     int  memLevel: "9" uses maximum memory for optimal speed.
     int  strategy))
    */
    if ((success = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY)) != Z_OK) {
        NSLog(@"CompressionError: in deflateInit2 %d", success);
        return nil;
    }


    if ((success = deflateSetDictionary(&strm, dictBytes, sizeof(dictBytes))) != Z_OK) {
      NSLog(@"CompressionError: in deflateSetDictionary %d", success);
      return nil;
    }

    // 16K chuncks for expansion
    NSMutableData *compressed = [NSMutableData dataWithLength:16384];

    do {

        if (strm.total_out >= [compressed length])
            [compressed increaseLengthBy: 16384];

        strm.next_out = (Bytef*)[compressed mutableBytes] + strm.total_out;
        strm.avail_out = (uInt)([compressed length] - strm.total_out);

        deflate(&strm, Z_FINISH);

    } while (strm.avail_out == 0);

    deflateEnd(&strm);

    if ((success != Z_OK) && (success != Z_STREAM_END)) {
        NSLog(@"CompressionError: after deflateEnd %d", success);
        return nil;
    }

    [compressed setLength: strm.total_out];
    return [NSData dataWithData: compressed];
}

Внутреннее декодирование

 public static String decompressMessage(byte [] data) {
 private static String dict = "#1#test#1234567890";

    String decompressed = "";

    try {
        Inflater decompresser = new Inflater(true);
        decompresser.setDictionary(dict.getBytes());
        decompresser.setInput(data, 0, data.length);
        byte[] result = new byte[100];
        int resultLength = decompresser.inflate(result);
        decompresser.end();

        decompressed = new String(result, 0, resultLength, Charset.forName("utf8"));

    } catch(DataFormatException ex) {
        return ex.getMessage();
    }
    return decompressed;
}
...