Russian Qt Forum
Ноябрь 24, 2024, 14:24 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Как быстро преобразовывать QByteArray в std::vector<unsigned char> и обратно?  (Прочитано 24602 раз)
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« : Ноябрь 12, 2014, 11:12 »

Долгое время пользовался таким наивным кодом для преобразований:

Код:
void convertByteArrayToVector(const QByteArray &qba, vector<unsigned char> &vec)
{
 unsigned int size=qba.size();

 vec.resize(size, 0);

 for(unsigned int i=0; i<size; i++)
  vec[i]=(unsigned char)qba[i];
}


void convertVectorToByteArray(const vector<unsigned char> &vec, QByteArray &qba)
{
 unsigned int size=vec.size();

 qba.resize(size);

 for(unsigned int i=0; i<size; i++)
  qba[i]=(unsigned char)vec[i];
}

Однако сейчас начал переносить приложение на Андроид, и там стала видна просадка. Профилирование показало, что одно из проблемных мест - вот эти функции (используются при передачи данных в функции шифрации-дешифрации). Их давно надо было переписать.


QByteArray в вектор

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

Я нашел такой код преобразования массива char в вектор:

Код:
vector<unsigned char> v;
v.resize(100);
memcpy(&v[0], someArrayOfSize100, 100 * sizeof(unsigned char));

Но не хочу его использовать, так как вектор - это более сложная структура чем массив char, у вектора элементы в памяти не обязательно идут друг за другом, и вот это memcpy() выглядит очень подозрительно.

Вопрос: как правильно и быстро преобразовать массив char в вектор char-ов?


Вектор в QByteArray

Обратная задача. Надо из вектора получить представление в виде массива char, и загнать его в QByteArray через метод fromRawData(). Основной вопрос в получении массива char.

Интернет предлагает:

Код:
vector<unsigned char> buf;
...
(char *)(&buf[0])

Или:

Код:
vector<unsigned char> buf;
...
unsigned char *p = &*buf.begin();

Но тут тоже вопрос - вектор не гарантирует последовательность элементов в памяти, как это так лихо получают ссылку на первый элемент и работают с данными?
Записан

Собираю информацию по крупицам
http://webhamster.ru
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #1 : Ноябрь 12, 2014, 11:16 »

Почему это не гарантирует? На сколько я помню, стандарт гарантирует расположение элементов вектора последовательно в памятию Поэтому юзай как написал и не парься.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
vulko
Гость
« Ответ #2 : Ноябрь 12, 2014, 12:14 »

Цитировать
Код:
vector<unsigned char> v;
v.resize(100);
memcpy(&v[0], someArrayOfSize100, 100 * sizeof(unsigned char));

так точно делать нельзя. вектор это не примитивный тип данных и попахивает memory corruption'ом.

не проще переделать на использование обычного массива вместо вектора?

как вариант:
Код:
vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #3 : Ноябрь 12, 2014, 12:21 »

Можно еще заюзать http://en.cppreference.com/w/cpp/container/vector/data
На счет размещения элементов:
Цитировать
The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets on regular pointers to elements. This means that a pointer to an element of a vector may be passed to any function that expects a pointer to an element of an array.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #4 : Ноябрь 12, 2014, 12:30 »

Цитировать
так точно делать нельзя.
С вектором так делать можно) Но можно это сделать и элегантнее)

Цитировать
// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}
Вот так делать точно не стоит.. Здесь на каждое итерирование back_inserter будет заново создавать новый блок для вектора, что очень не дёшево, и к тому же дефрагментируя память..
Гораздо логичнее делать так:
Код
C++ (Qt)
dataVec.assign(dataArray, dataArray+dataArraySize);
 

Ну и т.д. и т.п..

Цитировать
Долгое время пользовался таким наивным кодом для преобразований
Я бы эти две функции проще бы написал:
Код
C++ (Qt)
std::vector<char> convert(const QByteArray &b)
{
   return std::vector<char>(b.begin(), b.end());
}
 
QByteArray convert(const std::vector<char>& v)
{
   return QByteArray(v.data(), v.size());
}
 
Этот вариант раза в два будет быстрее, изначального варианта..
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vulko
Гость
« Ответ #5 : Ноябрь 12, 2014, 13:35 »

Цитировать
так точно делать нельзя.
С вектором так делать можно) Но можно это сделать и элегантнее)

&v
  • [/b], индекс не разглядел. Да, так и правда можно делать.


    Код:
    std::vector<char> convert(const QByteArray &b)
    {
        return std::vector<char>(b.begin(), b.end());
    }

    2 раза копировать будет.
    лучше по ссылке, либо без отдельной функции.

    ещё интересно как работает
    std::vector<char>(b.begin(), b.end())

    не быстрее ли сделать memcpy?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Ноябрь 12, 2014, 13:39 »

Цитировать
2 раза копировать будет.
Нет, в данном случае только один)

Два раза было бы, при такой реализации:
Код
C++ (Qt)
std::vector<char> convert(const QByteArray &b)
{
   std::vector<char> tmp(b.begin(), b.end());
   return tmp;
}
 


Цитировать
std::vector<char>(b.begin(), b.end())
Не исключено, что внутри там как раз memcpu или что-то аналогичное используется)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vulko
Гость
« Ответ #7 : Ноябрь 12, 2014, 13:43 »

Цитировать
2 раза копировать будет.
Нет, в данном случае только один)

Два раза было бы, при такой реализации:
Код
C++ (Qt)
std::vector<char> convert(const QByteArray &b)
{
   std::vector<char> tmp(b.begin(), b.end());
   return tmp;
}
 


одно и то же.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #8 : Ноябрь 12, 2014, 13:58 »

Цитировать
одно и то же.
Неееет) Не то же самое)

Код
C++ (Qt)
return std::vector<char>(b.begin(), b.end());
 
На первый взгляд похоже на вызов конструктора, но это не так..
Это синтаксис создания временного объекта: создать временный вектор и вернуть его.

В отличии от
Код
C++ (Qt)
std::vector<char> tmp(b.begin(), b.end());
   return tmp;
 
где происходит следующее: сначала будет создан объект tmp с вызовом конструктора, затем копирующий конструктор скопирует tmp в область памяти вне функции. Далее, для tmp будет вызван деструктор.

Для первого же варианта произойдёт следующее: компилятор строит объект непосредственно в области памяти возвращаемого значения. Для этого достаточно только вызова конструктора, копирующий конструктор не используется, а деструктор не вызывается, потому что локальный объект реально не создаётся.

Это примерно также, как если бы мы написали:
Код
C++ (Qt)
std::vector<char> tmp(b.begin(), b.end());
   return std::move(tmp);
 
« Последнее редактирование: Ноябрь 12, 2014, 14:02 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vulko
Гость
« Ответ #9 : Ноябрь 12, 2014, 14:03 »

Цитировать
одно и то же.
Неееет) Не то же самое)

Код
C++ (Qt)
return std::vector<char>(b.begin(), b.end());
 
На первый взгляд похоже на вызов конструктора, но это не так..
Это синтаксис создания временного объекта: создать временный вектор и вернуть его.

ну ты же сам пишешь, что создается временный объект. а потом он копируется.

"это вам не это" (с)
...в смысле не жаба, где все типы, кроме примитивных, ссылочные.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #10 : Ноябрь 12, 2014, 14:08 »

Цитировать
ну ты же сам пишешь, что создается временный объект. а потом он копируется.
Я пишу: когда компилятор встречает такую конструкцию, то она означает: "создать временный объект и вернуть его".
а далее:
Цитировать
компилятор строит объект непосредственно в области памяти возвращаемого значения. Для этого достаточно только вызова конструктора, копирующий конструктор не используется, а деструктор не вызывается, потому что локальный объект реально не создаётся.

А вообще загуглите: "оптимизация возвращаемого значения с++"
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vulko
Гость
« Ответ #11 : Ноябрь 12, 2014, 14:13 »

Цитировать
ну ты же сам пишешь, что создается временный объект. а потом он копируется.
Я пишу: когда компилятор встречает такую конструкцию, то она означает: "создать временный объект и вернуть его".
а далее:
Цитировать
компилятор строит объект непосредственно в области памяти возвращаемого значения. Для этого достаточно только вызова конструктора, копирующий конструктор не используется, а деструктор не вызывается, потому что локальный объект реально не создаётся.

А вообще загуглите: "оптимизация возвращаемого значения с++"

http://msdn.microsoft.com/en-us/library/ms364057(vs.80).aspx

слишком много но.
Записан
vulko
Гость
« Ответ #12 : Ноябрь 12, 2014, 14:21 »

Не люблю вики, но все же:

http://en.wikipedia.org/wiki/Return_value_optimization#Summary

Цитировать
Depending upon the compiler, and that compiler's settings...

это есть не хорошо. так что зачем на пустом месте создавать потенциальный гемор, когда можно прекрасно обойтись без отдельной функции, либо с параметром ссылкой на вектор.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #13 : Ноябрь 12, 2014, 14:29 »

Цитировать
Это ссылка не совсем о том, о чём я говорил..
В тех примерах нигде не было использована оптимизация возвращаемого значения (стандарт с++):
Код
C++ (Qt)
return vector(...)
 
Там говориться лишь о том, как MSVC оптимизирует такую ситуацию:
Код
C++ (Qt)
vector tmp;
return tmp;
 
 

Но это разные вещи..

Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vulko
Гость
« Ответ #14 : Ноябрь 12, 2014, 14:35 »

Цитировать
Это ссылка не совсем о том, о чём я говорил..
В тех примерах нигде не было использована оптимизация возвращаемого значения (стандарт с++):
Код
C++ (Qt)
return vector(...)
 
Там говориться лишь о том, как MSVC оптимизирует такую ситуацию:
Код
C++ (Qt)
vector tmp;
return tmp;
 
 

Но это разные вещи..



это NRVO, т.е. Named RVO. Работает также, только для случаев
type fname() {
    type a;
    return a;
}

Читай выше приводил вырезки из вики. Это не стандарт. Стандарт разрешает компилятору так делать, но не обязывает.
При этом совсем не обязательно что без флагов оптимизации оно будет работать. И совсем не обязательно что в любом компиляторе одинаково.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.263 секунд. Запросов: 23.