Могу ли я сравнить объект на основе сериализации proto buf - PullRequest
1 голос
/ 16 ноября 2011

У меня есть несколько объектов модели, которые я сохраняю в БД, сериализованной с protobuf. Я хочу сравнить версию, которую я сохраню, с существующей, чтобы избежать двухкратного добавления одной и той же версии.

В идеале я должен

byte[] existingBlob = GetFromDBExistingModelObject();
ModelType existingModel = existingBlob.Deserialize();
if (!model.Equals(existingModel))
{
    byte[] serializedModel = model.Serialize();
    Save(serializedModel); //Save in DB the new blob
}

Однако мне придется реализовать .Equals на каждом объекте модели, и это будет довольно болезненно. Я хотел бы сделать

byte[] existingBlob = GetFromDBExistingModelObject();
byte[] serializedModel = model.Serialize();
if (!compareBlob(existingBlob, serializedModel)
{
    Save(serializedModel);
}

private bool compareBlob(byte[] existingBlob, byte[] serializedModel)
{
    if (serializedModel.Length != existingBlob.Length)
    {
        return false;
    }

    return !serializedModel.Where((t, i) => t != existingBlob[i]).Any();
}

Я также делаю это для производительности, потому что я не десериализую existingBlob

Что вы думаете об этой реализации? Как вы думаете, я могу положиться на это сравнение? Я использую protobuf для сериализации.

Спасибо за ваш комментарий.

1 Ответ

1 голос
/ 16 ноября 2011

protobuf-net будет производить предсказуемый результат, но строго говоря , что не гарантируется спецификацией;- есть 2 крайних случая (порядок полей и субнормальные формы для кодирования varint), которые технически могут выдавать разные выходные данные с одинаковым значением, но protobuf-net всегда будетпроизводить тот же вывод в настоящее время.

I am играет с добавлением опции к сознательно использует суб-нормальные формы varint, чтобы избежать некоторого перестановки памяти, но это будет вариант- только.

Итак;если вы не строите свои двоичные файлы путем добавления (protobuf - это добавляемый формат, но, очевидно, все ставки отключены, если вы добавляете в произвольных порядках), тогда да: данные на проводе должныбыть предсказуемым, и вы можете сравнить последовательность байтов для проверки на равенство.

В качестве небольшого примечания я бы порекомендовал бы здесь обычный цикл for для эффективности:

if (serializedModel.Length != existingBlob.Length)
{
    return false;
}
for(int i = 0 ; i < serializedModel.Length ; i++)
    if(serializedModel[i] != existingBlob[i]) return false;
return true;

(если вы особенно без ума от скорости, вы можете даже использовать unsafe код и сравнить его как int* или long* вместо этого (принимая 1/4 или 1/8 тестов), ипросто проверьте последние несколько байт вручную)

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


: в частности, битовая последовательность 10000000 или 00000000 на «большом конце» varint просто означает «и больше нулей на большом конце» (с дополнительными данными или без них), поэтомуне влияет на количество;следовательно, любое (разумное) число 0x80 0x80 0x00 в конце varint не меняет результат;есть вариант использования, в котором это можно было бы использовать, чтобы избежать необходимости перемещать данные, преднамеренно используя в качестве префикса длины переменный размер, превышающий размер.

...