Перейти к содержимому

Buffer c что это

  • автор:

setvbuf

buffer
Выделенный пользователем буфер.

mode
Режим буферизации.

size
Размер буфера в байтах. Допустимый диапазон: 2 size size , округляется вниз до ближайшего числа, кратного 2.

Возвращаемое значение

Возвращает 0 в случае успеха.

Если stream значение NULL равно или size mode нет в допустимом изменении, вызывается обработчик недопустимых параметров, как описано в разделе проверки параметров. Если продолжение выполнения разрешено, эта функция возвращает –1 и задает для errno значение EINVAL .

Дополнительные сведения об этих и других кодах ошибок см. в разделе errno , _doserrno _sys_errlist и _sys_nerr .

Замечания

Функция setvbuf позволяет программе управлять и буферизацией, и размером буфера для stream . stream должен ссылаться на открытый файл, который не прошел операцию ввода-вывода после открытия. Массив, на который указывает buffer , используется в качестве буфера, если buffer только не используется setvbuf NULL автоматически выделенный буфер длины size /2 * 2 байта.

Должен быть установлен режим _IOFBF , _IOLBF , или _IONBF . Если mode имеет значение _IOFBF или _IOLBF , то size используется в качестве размера буфера. Если mode это _IONBF так, поток не учитывается, и оба size и buffer игнорируются. Переменные для функции mode и их значения:

Значение mode Значение
_IOFBF Полная буферизация; то есть buffer используется в качестве буфера и size используется в качестве размера буфера. Если buffer это NULL так, этот режим использует автоматически выделенный буфер, который имеет size длину байтов.
_IOLBF Для некоторых систем этот режим обеспечивает буферизацию строк. Однако для Win32 такое поведение аналогично _IOFBF , то есть полной буферизации.
_IONBF Буфер не используется, независимо от функций buffer или size .

По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью «Глобальное состояние» в CRT.

Требования

Маршрут Обязательный заголовок
setvbuf

Дополнительные сведения о совместимости см. в разделе Совместимость.

Библиотеки

Пример

// crt_setvbuf.c // This program opens two streams: stream1 // and stream2. It then uses setvbuf to give stream1 a // user-defined buffer of 1024 bytes and stream2 no buffer. // #include int main( void ) < char buf[1024]; FILE *stream1, *stream2; if( fopen_s( &stream1, "data1", "a" ) == 0 && fopen_s( &stream2, "data2", "w" ) == 0 ) < if( setvbuf( stream1, buf, _IOFBF, sizeof( buf ) ) != 0 ) printf( "Incorrect type or size of buffer for stream1\n" ); else printf( "'stream1' now has a buffer of 1024 bytes\n" ); if( setvbuf( stream2, NULL, _IONBF, 0 ) != 0 ) printf( "Incorrect type or size of buffer for stream2\n" ); else printf( "'stream2' now has no buffer\n" ); _fcloseall(); >> 
'stream1' now has a buffer of 1024 bytes 'stream2' now has no buffer 

Получение указателей на буферы данных (C++/CX)

В среде выполнения Windows интерфейс Windows::Storage::Streams::IBuffer предоставляет не зависящий от языка и основанный на потоках механизм доступа к буферам данных. В C++ можно получить необработанный указатель на базовый массив байтов с помощью интерфейса IBufferByteAccess библиотеки среды выполнения Windows, определенный в файле robuffer.h. Благодаря такому подходу можно изменить массив байтов на месте без создания лишний копий данных.

На следующей схеме показан элемент изображения XAML, источником которого является Windows::UI::Xaml::Media::Imaging WriteableBitmap. Клиентские приложения, написанные на любом языке, могут передавать ссылку на WriteableBitmap в код C++, а затем код C++ может использовать эту ссылку для обращения к соответствующему буферу. В приложении универсальная платформа Windows, написанном на C++, можно использовать функцию в следующем примере непосредственно в исходном коде, не упаковывая его в компонент среда выполнения Windows.

Diagram showing a code component that accesses pixel data directly.

GetPointerToPixelData

Следующий метод принимает Windows::Storage::Streams::IBuffer и возвращает необработанный указатель на соответствующий массив байтов. Чтобы вызвать функцию, передайте свойство WriteableBitmap::PixelBuffer .

#include #include using namespace Windows::Storage::Streams; using namespace Microsoft::WRL; typedef uint8 byte; // Retrieves the raw pixel data from the provided IBuffer object. // Warning: The lifetime of the returned buffer is controlled by // the lifetime of the buffer object that's passed to this method. // When the buffer has been released, the pointer becomes invalid // and must not be used. byte* Class1::GetPointerToPixelData(IBuffer^ pixelBuffer, unsigned int *length) < if (length != nullptr) < *length = pixelBuffer ->Length; > // Query the IBufferByteAccess interface. ComPtr bufferByteAccess; reinterpret_cast( pixelBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); // Retrieve the buffer data. byte* pixels = nullptr; bufferByteAccess->Buffer(&pixels); return pixels; > 

Полный пример

Ниже показано, как создать приложение универсальная платформа Windows C#, которое передает WriteableBitmap библиотеку DLL компонента C++ среда выполнения Windows. Код C++ получает указатель на буфер пикселя, выполняющий простое изменение изображения на месте. В качестве альтернативы можно создать клиентское приложение на языке Visual Basic, JavaScript или C++, а не на C#. При использовании C++ нет необходимости в компоненте DLL; можно просто добавить эти методы непосредственно в класс MainPage или в какие-либо другие классы.

Создание клиента
  1. Используйте шаблон проекта пустого приложения для создания приложения универсальная платформа Windows C#.
  2. В файле MainPage.xaml
  3. Используйте это XAML-файл, чтобы заменить элемент Grid :
    Добавьте следующие объявления пространств имен:
using Windows.Storage; using Windows.Storage.FileProperties; using Windows.UI.Xaml.Media.Imaging; using Windows.Storage.Streams; using Windows.Storage.Pickers; 
private WriteableBitmap m_bm; 
async protected override void OnNavigatedTo(NavigationEventArgs e) < FileOpenPicker openPicker = new FileOpenPicker(); openPicker.ViewMode = PickerViewMode.Thumbnail; openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; openPicker.FileTypeFilter.Add(".jpg"); openPicker.FileTypeFilter.Add(".jpeg"); openPicker.FileTypeFilter.Add(".png"); StorageFile file = await openPicker.PickSingleFileAsync(); if (file != null) < // Get the size of the image for the WriteableBitmap constructor. ImageProperties props = await file.Properties.GetImagePropertiesAsync(); m_bm = new WriteableBitmap((int)props.Height, (int)props.Width); m_bm.SetSource(await file.OpenReadAsync()); Pic.Source = m_bm; >else < // Handle error. >> 
async private void Button_Click_1(object sender, RoutedEventArgs e)
Создание компонента C++
  1. Добавьте новый компонент среда выполнения Windows C++ в существующее решение и назовите его ImageManipCPP . Добавьте ссылку на него в проект C#, щелкнув этот проект правой кнопкой мыши в Обозревателе решений и выбрав Добавить, Ссылка.
  2. В файле Class1.h
    1. Добавьте определение typedef во вторую строку сразу после #pragma once .
    typedef uint8 byte; 
    [Windows::Foundation::Metadata::WebHostHidden] 
    Windows::Foundation::IAsyncAction^ Negativize(Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ bm); 
    #include #include #include using namespace Windows::Storage; using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::Storage::Streams; using namespace Microsoft::WRL; 
    IAsyncAction^ Class1::Negativize(WriteableBitmap^ bm) < unsigned int length; byte* sourcePixels = GetPointerToPixelData(bm->PixelBuffer, &length); const unsigned int width = bm->PixelWidth; const unsigned int height = bm->PixelHeight; return create_async([this, width, height, sourcePixels] < byte* temp = sourcePixels; for(unsigned int k = 0; k < height; k++) < for (unsigned int i = 0; i < (width * 4); i += 4) < int pos = k * (width * 4) + (i); temp[pos] = ~temp[pos]; temp[pos + 1] = ~temp[pos + 1] / 3; temp[pos + 2] = ~temp[pos + 2] / 2; temp[pos + 3] = ~temp[pos + 3]; >> >); > 

    Примечание. Этот метод может выполняться быстрее, если для параллелизации операции используется AMP или библиотека параллельных шаблонов.

    Строковый буфер Си

    Какой есть на Си строковый буфер вроде stringstream на C++, и как им пользоваться?

    Отслеживать

    задан 14 мая 2016 в 19:58

    9,848 5 5 золотых знаков 29 29 серебряных знаков 58 58 бронзовых знаков

    1 ответ 1

    Сортировка: Сброс на вариант по умолчанию

    В стандарте POSIX 2008 есть весьма удобная функция: open_memstream(). Вы можете сделать так:

    // здесь в итоге появятся записанные значения и размер текста: char* buffer = NULL; size_t bufferSize = 0; // создаём поток с указателями для выходных значений: FILE* myStream = open_memstream(&buffer, &bufferSize); // можно записывать в поток как в обычный вывод: fprintf(myStream, "You can output anything to myStream, just as you can with stdout.\n"); // можно добавить что-либо ещё с неизвестным размером в других функциях: myComplexPrintFunction(myStream); // закрытие потока перенесёт данные и размер в buffer и bufferSize: fclose(myStream); // теперь можно делать что угодно с полученной строкой: printf("I can do anything with the resulting string now. It is: \"%s\"\n", buffer); // ну и не забывайте чистить память: free(buffer); 

    Используем черную магию для создания быстрого кольцевого буфера

    Вчера я заглянул на страницу Википедии, посвященную кольцевому буферу (circular buffer), и был заинтригован предполагаемой техникой оптимизации, с которой до этого не был знаком:

    Реализация кольцевого буфера может быть оптимизирована путем отображения нижележащего буфера в двух смежных областях виртуальной памяти. (Естественно, длина нижележащего буфера должна в таком случае равняться некоторому размеру кратному размеру страницы системы.) Чтение и запись в кольцевой буфер могут выполняться в этой реализации с большей эффективностью посредством прямого доступа к памяти; те обращения, которые выходят за пределы первой области виртуальной памяти, автоматически переходят в начало нижележащего буфера. Когда смещение чтения продвигается во вторую область виртуальной памяти, оба смещения — чтения и записи — уменьшаются на длину нижележащего буфера.

    В рамках реализации кольцевого буфера нам необходимо обработать случай, когда сообщение попадает на «разрыв» в очереди и должно быть перенесено (wrap around). Очевидная реализация записи в кольцевой буфер может полагаться на побайтовую запись и выглядеть примерно так:

    void put(queue_t *q, uint8_t *data, size_t size) < for(size_t i = 0; i < size; i++)< q->buffer[(q->tail + i) % q->buffer_size] = data[i]; > q->tail = (q->tail + size) % q->buffer_size; >

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

    В следующих двух разделах рассматривается конструкция кольцевого буфера и логика работы виртуальной памяти соответственно. Если вы не нуждаетесь в освежении этой информации, можете смело пропустить их.

    Кольцевой буфер

    Кольцевой (циклический) буфер — это удобный подход к хранению потоковых данных, которые создаются в реальном времени и вскоре после этого потребляются. Он «закольцован» для того, чтобы новые данные могли постоянно перезанимать пространство, ранее занимаемое данными, которые с тех пор уже были использованы.

    Кольцевой буфер обычно инкрементируется посредством сохранения двух указателей (или индексов): указателя чтения (я назову его head — голова) и указателя записи (или tail — хвост). Очевидно, мы записываем в буфер по tail , а читаем из head . Структура может быть определена следующим образом:

    typedef struct < uint8_t *buffer; size_t buffer_size; size_t head; size_t tail; size_t bytes_avail; >queue_t;

    Учитывая это, мы можем написать простые методы чтения ( get ) и записи ( put ), используя побайтовый доступ:

    bool put(queue_t *q, uint8_t *data, size_t size) < if(q->buffer_size - q->bytes_avail < size)< return false; >for(size_t i = 0; i < size; i++)< q->buffer[(q->tail + i) % q->buffer_size] = data[i]; > q->tail = (q->tail + size) % q->buffer_size; q->bytes_avail += size; return true; > bool get(queue_t *q, uint8_t *data, size_t size) < if(q->bytes_avail < size)< return false; >for(size_t i = 0; i < size; i++)< data[i] = q->buffer[(q->head + i) % q->buffer_size]; > q->head = (q->head + size) % q->buffer_size; q->bytes_avail -= size; return true; >

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

    Побайтовый тип доступа и модульная арифметика, встроенные в каждую итерацию цикла, делают этот код ужасающе медленным. Вместо этого мы можем реализовать каждую операцию чтения или записи через два вызова memcpy , где один обрабатывает часть сообщения в конце буфера, а другой обрабатывает часть в начале, если мы закольцевались.

    static inline off_t min(off_t a, off_t b) < return a < b ? a : b; >bool put(queue_t *q, uint8_t *data, size_t size) < if(q->buffer_size - q->bytes_avail < size)< return false; >const size_t part1 = min(size, q->;buffer_size - q->;tail); const size_t part2 = size - part1; memcpy(q->;buffer + q->;tail, data, part1); memcpy(q->;buffer, data + part1, part2); q->;tail = (q->;tail + size) % q->;buffer_size; q->;bytes_avail += size; return true; > bool get(queue_t *q, uint8_t *data, size_t size) < if(q->bytes_avail < size)< return false; >const size_t part1 = min(size, q->;buffer_size - q->;head); const size_t part2 = size - part1; memcpy(data, q->;buffer + q->;head, part1); memcpy(data + part1, q->;buffer, part2); q->;head = (q->;head + size) % q->;buffer_size; q->;bytes_avail -= size; return true; >

    А вот пример использования этого кольцевого буфера:

    int main() < queue_t queue; queue.buffer = malloc(128); queue.buffer_size = 128; queue.head = 0; queue.tail = 0; queue.bytes_avail = 0; put(&q, "hello ", 6); put(&q, "world\n", 7); char s[13]; get(&q, (uint8_t *) s, 13); printf(s); // prints "hello world" >

    Наш код прост и отлично работает. Но почему бы не усложнить его немного?

    Встречайте таблицу страниц

    На заре персональных компьютеров компьютеры могли одновременно запускать только одну программу. Какую бы программу мы ни запустили, она будет иметь полный прямой доступ к физической памяти. Оказывается, если мы хотим запустить несколько программ вместе, они будут бороться за то, какие области этой памяти они хотят использовать. Решением этого конфликта является виртуальная память, благодаря которой каждая программа считает, что она контролирует всю память, но на самом деле операционная система решает, кто какую память получает.

    Ключом к виртуальной памяти является таблица страниц, с помощью которой операционная система сообщает ЦП о сопоставлениях между виртуальными и физическими страницами. Страница — это непрерывная выровненная область памяти (обычно) фиксированного размера, скажем, около 4 КиБ. Когда программе требуется доступ к некоторой (виртуальной) памяти, ЦП определяет, какой странице она принадлежит, а затем использует таблицу страниц для индексации в физической памяти.

    Таблица страниц для некоторой программы А может выглядеть следующим:

    Номер виртуальной страницы

    Номер физической страницы

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *