📄 metadata_iterators.c
字号:
FLAC__ASSERT(0 != chain);
FLAC__ASSERT(0 != node);
if(node == chain->head)
chain->head = node->next;
else
node->prev->next = node->next;
if(node == chain->tail)
chain->tail = node->prev;
else
node->next->prev = node->prev;
if(0 != chain->tail)
chain->tail->data->is_last = true;
chain->nodes--;
}
static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
{
chain_remove_node_(chain, node);
node_delete_(node);
}
static unsigned chain_calculate_length_(FLAC__Metadata_Chain *chain)
{
const FLAC__Metadata_Node *node;
unsigned length = 0;
for(node = chain->head; node; node = node->next)
length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
return length;
}
static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node)
{
FLAC__ASSERT(0 != node);
FLAC__ASSERT(0 != node->data);
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != iterator->current);
FLAC__ASSERT(0 != iterator->chain);
FLAC__ASSERT(0 != iterator->chain->head);
FLAC__ASSERT(0 != iterator->chain->tail);
node->data->is_last = false;
node->prev = iterator->current->prev;
node->next = iterator->current;
if(0 == node->prev)
iterator->chain->head = node;
else
node->prev->next = node;
iterator->current->prev = node;
iterator->chain->nodes++;
}
static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node)
{
FLAC__ASSERT(0 != node);
FLAC__ASSERT(0 != node->data);
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != iterator->current);
FLAC__ASSERT(0 != iterator->chain);
FLAC__ASSERT(0 != iterator->chain->head);
FLAC__ASSERT(0 != iterator->chain->tail);
iterator->current->data->is_last = false;
node->prev = iterator->current;
node->next = iterator->current->next;
if(0 == node->next)
iterator->chain->tail = node;
else
node->next->prev = node;
node->prev->next = node;
iterator->chain->tail->data->is_last = true;
iterator->chain->nodes++;
}
/* return true iff node and node->next are both padding */
static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
{
if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) {
const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length;
node->data->length += growth;
chain_delete_node_(chain, node->next);
return true;
}
else
return false;
}
/* Returns the new length of the chain, or 0 if there was an error. */
/* WATCHOUT: This can get called multiple times before a write, so
* it should still work when this happens.
*/
/* WATCHOUT: Make sure to also update the logic in
* FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes.
*/
static unsigned chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding)
{
unsigned current_length = chain_calculate_length_(chain);
if(use_padding) {
/* if the metadata shrank and the last block is padding, we just extend the last padding block */
if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
const unsigned delta = chain->initial_length - current_length;
chain->tail->data->length += delta;
current_length += delta;
FLAC__ASSERT(current_length == chain->initial_length);
}
/* if the metadata shrank more than 4 bytes then there's room to add another padding block */
else if(current_length + FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) {
FLAC__StreamMetadata *padding;
FLAC__Metadata_Node *node;
if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) {
chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
return 0;
}
padding->length = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length);
if(0 == (node = node_new_())) {
FLAC__metadata_object_delete(padding);
chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
return 0;
}
node->data = padding;
chain_append_node_(chain, node);
current_length = chain_calculate_length_(chain);
FLAC__ASSERT(current_length == chain->initial_length);
}
/* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */
else if(current_length > chain->initial_length) {
const unsigned delta = current_length - chain->initial_length;
if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
/* if the delta is exactly the size of the last padding block, remove the padding block */
if(chain->tail->data->length + FLAC__STREAM_METADATA_HEADER_LENGTH == delta) {
chain_delete_node_(chain, chain->tail);
current_length = chain_calculate_length_(chain);
FLAC__ASSERT(current_length == chain->initial_length);
}
/* if there is at least 'delta' bytes of padding, trim the padding down */
else if(chain->tail->data->length >= delta) {
chain->tail->data->length -= delta;
current_length -= delta;
FLAC__ASSERT(current_length == chain->initial_length);
}
}
}
}
return current_length;
}
static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb)
{
FLAC__Metadata_Node *node;
FLAC__ASSERT(0 != chain);
/* we assume we're already at the beginning of the file */
switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) {
case 0:
break;
case 1:
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
return false;
case 2:
chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
return false;
case 3:
chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE;
return false;
default:
FLAC__ASSERT(0);
return false;
}
{
FLAC__int64 pos = tell_cb(handle);
if(pos < 0) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
return false;
}
chain->first_offset = (long)pos;
}
{
FLAC__bool is_last;
FLAC__MetadataType type;
unsigned length;
do {
node = node_new_();
if(0 == node) {
chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
return false;
}
if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
return false;
}
node->data = FLAC__metadata_object_new(type);
if(0 == node->data) {
node_delete_(node);
chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
return false;
}
node->data->is_last = is_last;
node->data->length = length;
chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data));
if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) {
node_delete_(node);
return false;
}
chain_append_node_(chain, node);
} while(!is_last);
}
{
FLAC__int64 pos = tell_cb(handle);
if(pos < 0) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
return false;
}
chain->last_offset = (long)pos;
}
chain->initial_length = chain_calculate_length_(chain);
return true;
}
static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb)
{
FLAC__Metadata_Node *node;
FLAC__ASSERT(0 != chain);
FLAC__ASSERT(0 != chain->head);
if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) {
chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
return false;
}
for(node = chain->head; node; node = node->next) {
if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) {
chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
return false;
}
if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) {
chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
return false;
}
}
/*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
return true;
}
static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain)
{
FILE *file;
FLAC__bool ret;
FLAC__ASSERT(0 != chain->filename);
if(0 == (file = fopen(chain->filename, "r+b"))) {
chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
return false;
}
/* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */
ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_);
fclose(file);
return ret;
}
static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix)
{
FILE *f, *tempfile;
char *tempfilename;
FLAC__Metadata_SimpleIteratorStatus status;
const FLAC__Metadata_Node *node;
FLAC__ASSERT(0 != chain);
FLAC__ASSERT(0 != chain->filename);
FLAC__ASSERT(0 != chain->head);
/* copy the file prefix (data up to first metadata block */
if(0 == (f = fopen(chain->filename, "rb"))) {
chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
return false;
}
if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) {
chain->status = get_equivalent_status_(status);
cleanup_tempfile_(&tempfile, &tempfilename);
return false;
}
if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) {
chain->status = get_equivalent_status_(status);
cleanup_tempfile_(&tempfile, &tempfilename);
return false;
}
/* write the metadata */
for(node = chain->head; node; node = node->next) {
if(!write_metadata_block_header_(tempfile, &status, node->data)) {
chain->status = get_equivalent_status_(status);
return false;
}
if(!write_metadata_block_data_(tempfile, &status, node->data)) {
chain->status = get_equivalent_status_(status);
return false;
}
}
/*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
/* copy the file postfix (everything after the metadata) */
if(0 != fseek(f, chain->last_offset, SEEK_SET)) {
cleanup_tempfile_(&tempfile, &tempfilename);
chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
return false;
}
if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) {
cleanup_tempfile_(&tempfile, &tempfilename);
chain->status = get_equivalent_status_(status);
return false;
}
/* move the tempfile on top of the original */
(void)fclose(f);
if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status))
return false;
return true;
}
/* assumes 'handle' is already at beginning of file */
static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb)
{
FLAC__Metadata_SimpleIteratorStatus status;
const FLAC__Metadata_Node *node;
FLAC__ASSERT(0 != chain);
FLAC__ASSERT(0 == chain->filename);
FLAC__ASSERT(0 != chain->head);
/* copy the file prefix (data up to first metadata block */
if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) {
chain->status = get_equivalent_status_(status);
return false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -