* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* lz4_file.cpp
* All interface of LZ4 compress and write file
*
* IDENTIFICATION
* src/gausskernel/storage/file/lz4_file.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include <fcntl.h>
#include "access/xact.h"
#include "miscadmin.h"
#include "storage/smgr/fd.h"
#include "lz4.h"
#include "storage/lz4_file.h"
#include "utils/memutils.h"
#include "executor/instrument.h"
#define COMPRESS_DATA_SIZE 8
#define COMPRESS_FILESIZE 0x7FFFFFFFFFFFFFFF
* @Description: create the temp file
* @in interXact - to mark the temp file will automatically deleted at end of transaction of not
* @return - file pointer
*/
LZ4File* LZ4FileCreate(bool interXact)
{
LZ4File* lz4File = (LZ4File*)palloc(sizeof(LZ4File));
lz4File->Reset();
lz4File->srcBuf = (char*)palloc(LZ4FileSrcBufSize);
lz4File->file = OpenTemporaryFile(interXact);
lz4File->isInterXact = interXact;
return lz4File;
}
* @Description: flush the data to file with lz4 compress
* @in lz4File - file pointer
* @return - void
*/
static void LZ4FileFlush(LZ4File* lz4File)
{
int boundSize = LZ4_COMPRESSBOUND(lz4File->srcDataSize);
if (boundSize > lz4File->compressBufSize - COMPRESS_DATA_SIZE) {
if (lz4File->compressBufSize > 0)
pfree(lz4File->compressBuf);
lz4File->compressBuf = (char*)palloc((Size)(boundSize + COMPRESS_DATA_SIZE));
lz4File->compressBufSize = boundSize + COMPRESS_DATA_SIZE;
}
* lz4File->compressBuf
* 4 bytes, compressed data size
* 4 bytes, src data size
* lz4File->compressBuf + 8, compressed data
*/
int outSize = LZ4_compress_default(lz4File->srcBuf,
lz4File->compressBuf + COMPRESS_DATA_SIZE,
lz4File->srcDataSize,
LZ4_compressBound(lz4File->srcDataSize));
*(int*)lz4File->compressBuf = outSize;
*(int*)(lz4File->compressBuf + sizeof(int)) = lz4File->srcDataSize;
if (lz4File->curOffset > COMPRESS_FILESIZE - outSize - COMPRESS_DATA_SIZE) {
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write to temporary file: the file size exceeds the max size: %ldBYTE",
COMPRESS_FILESIZE)));
}
int bytestowrite =
FilePWrite(lz4File->file, lz4File->compressBuf, outSize + COMPRESS_DATA_SIZE, lz4File->curOffset,
0, 0, lz4File->isInterXact);
if (bytestowrite != outSize + COMPRESS_DATA_SIZE) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to temporary file: %m")));
}
u_sess->instr_cxt.pg_buffer_usage->temp_blks_written++;
lz4File->curOffset += bytestowrite;
}
* @Description: write data to file
* @in lz4File - file pointer
* @in buffer - the data to be written
* @in size - data size
* @return - written size
*/
size_t LZ4FileWrite(LZ4File* lz4File, char* buffer, size_t size)
{
Assert(lz4File);
int rc = EOK;
size_t nwritten = 0;
size_t nthistime;
while (size > 0) {
if (lz4File->srcDataSize >= LZ4FileSrcBufSize) {
LZ4FileFlush(lz4File);
lz4File->srcDataSize = 0;
}
nthistime = (size_t)(LZ4FileSrcBufSize - lz4File->srcDataSize);
if (nthistime > size)
nthistime = size;
Assert(nthistime > 0);
rc = memcpy_s(lz4File->srcBuf + lz4File->srcDataSize, nthistime, buffer, nthistime);
securec_check_ss(rc, "", "");
lz4File->srcDataSize += (int)nthistime;
buffer = buffer + nthistime;
size -= nthistime;
nwritten += nthistime;
}
return nwritten;
}
* @Description: read data from file
* @in lz4File - file pointer
* @in buffer - the read data to be storage
* @in size - data size
* @return - read size
*/
size_t LZ4FileRead(LZ4File* lz4File, char* buffer, size_t size)
{
size_t nread = 0;
size_t nthistime;
while (size > 0) {
if (lz4File->readOffset >= lz4File->srcDataSize) {
int len[2];
int nbytes = FilePRead(lz4File->file, (char*)len, COMPRESS_DATA_SIZE, lz4File->curOffset,
0, lz4File->isInterXact);
if (0 == nbytes)
return nread;
if (COMPRESS_DATA_SIZE != nbytes) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not read from temporary file: %m")));
}
lz4File->curOffset += COMPRESS_DATA_SIZE;
int compressSize = len[0];
int srcSize = len[1];
if (lz4File->compressBufSize < compressSize) {
lz4File->compressBuf = (char*)repalloc(lz4File->compressBuf, (Size)compressSize);
lz4File->compressBufSize = compressSize;
}
if (FilePRead(lz4File->file, lz4File->compressBuf, compressSize, lz4File->curOffset,
0, lz4File->isInterXact) != compressSize) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not read from temporary file: %m")));
}
int decompressedSize = LZ4_decompress_safe(lz4File->compressBuf, lz4File->srcBuf, compressSize, srcSize);
if (decompressedSize != srcSize) {
Assert(false);
}
lz4File->readOffset = 0;
lz4File->srcDataSize = srcSize;
u_sess->instr_cxt.pg_buffer_usage->temp_blks_read++;
lz4File->curOffset += compressSize;
}
nthistime = (size_t)(lz4File->srcDataSize - lz4File->readOffset);
if (nthistime > size)
nthistime = size;
Assert(nthistime > 0);
int rc = EOK;
rc = memcpy_s(buffer, nthistime, lz4File->srcBuf + lz4File->readOffset, nthistime);
securec_check_ss(rc, "", "");
lz4File->readOffset += (off_t)nthistime;
buffer = buffer + nthistime;
size -= nthistime;
nread += nthistime;
}
return nread;
}
* @Description: close the temp file
* @in lz4File - file pointer
* @return - void
*/
void LZ4FileClose(LZ4File* lz4File)
{
if (lz4File->file > 0) {
FileClose(lz4File->file, lz4File->isInterXact);
lz4File->file = FILE_INVALID;
if (lz4File->srcBuf) {
pfree(lz4File->srcBuf);
lz4File->srcBuf = NULL;
}
if (lz4File->compressBuf) {
pfree(lz4File->compressBuf);
lz4File->compressBuf = NULL;
}
}
pfree(lz4File);
}
* @Description: seek to the start of the file
* @in lz4File - file pointer
* @return - void
*/
void LZ4FileRewind(LZ4File* lz4File)
{
if (lz4File->srcDataSize > 0) {
LZ4FileFlush(lz4File);
}
lz4File->readOffset = 0;
lz4File->srcDataSize = 0;
lz4File->curOffset = 0;
}
* @Description: just flush the buffer-data into disk
* @in lz4File: file handler
* @return: void
*
* Note:
* 1) Differs from LZ4FileSeek():
* After calling LZ4FileClearBuffer(), we can still write/read
* from the last position of the file.
*
* 2) Aim: to prepare for release the buffer
*/
void LZ4FileClearBuffer(LZ4File* lz4File)
{
Assert(lz4File != NULL);
if (lz4File->srcDataSize > 0) {
LZ4FileFlush(lz4File);
}
lz4File->readOffset = 0;
lz4File->srcDataSize = 0;
}