InputStream: use int64_t instead of goffset

Decouple some more from GLib.
This commit is contained in:
Max Kellermann 2013-10-17 09:43:55 +02:00
parent 24780d99e6
commit 05de2e998c
20 changed files with 78 additions and 64 deletions

View File

@ -23,9 +23,8 @@
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include <glib.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
struct config_param; struct config_param;
struct input_stream; struct input_stream;
@ -33,6 +32,8 @@ class Error;
struct Tag; struct Tag;
struct InputPlugin { struct InputPlugin {
typedef int64_t offset_type;
const char *name; const char *name;
/** /**
@ -85,7 +86,7 @@ struct InputPlugin {
size_t (*read)(struct input_stream *is, void *ptr, size_t size, size_t (*read)(struct input_stream *is, void *ptr, size_t size,
Error &error); Error &error);
bool (*eof)(struct input_stream *is); bool (*eof)(struct input_stream *is);
bool (*seek)(struct input_stream *is, goffset offset, int whence, bool (*seek)(struct input_stream *is, offset_type offset, int whence,
Error &error); Error &error);
}; };

View File

@ -95,7 +95,7 @@ input_stream::CheapSeeking() const
} }
bool bool
input_stream::Seek(goffset _offset, int whence, Error &error) input_stream::Seek(offset_type _offset, int whence, Error &error)
{ {
if (plugin.seek == nullptr) if (plugin.seek == nullptr)
return false; return false;
@ -104,7 +104,7 @@ input_stream::Seek(goffset _offset, int whence, Error &error)
} }
bool bool
input_stream::LockSeek(goffset _offset, int whence, Error &error) input_stream::LockSeek(offset_type _offset, int whence, Error &error)
{ {
if (plugin.seek == nullptr) if (plugin.seek == nullptr)
return false; return false;

View File

@ -26,9 +26,8 @@
#include <string> #include <string>
#include <glib.h>
#include <assert.h> #include <assert.h>
#include <stdint.h>
class Cond; class Cond;
class Error; class Error;
@ -36,6 +35,8 @@ struct Tag;
struct InputPlugin; struct InputPlugin;
struct input_stream { struct input_stream {
typedef int64_t offset_type;
/** /**
* the plugin which implements this input stream * the plugin which implements this input stream
*/ */
@ -80,12 +81,12 @@ struct input_stream {
/** /**
* the size of the resource, or -1 if unknown * the size of the resource, or -1 if unknown
*/ */
goffset size; offset_type size;
/** /**
* the current offset within the stream * the current offset within the stream
*/ */
goffset offset; offset_type offset;
/** /**
* the MIME content type of the resource, or empty if unknown. * the MIME content type of the resource, or empty if unknown.
@ -173,14 +174,14 @@ struct input_stream {
} }
gcc_pure gcc_pure
goffset GetSize() const { offset_type GetSize() const {
assert(ready); assert(ready);
return size; return size;
} }
gcc_pure gcc_pure
goffset GetOffset() const { offset_type GetOffset() const {
assert(ready); assert(ready);
return offset; return offset;
@ -208,13 +209,13 @@ struct input_stream {
* @param offset the relative offset * @param offset the relative offset
* @param whence the base of the seek, one of SEEK_SET, SEEK_CUR, SEEK_END * @param whence the base of the seek, one of SEEK_SET, SEEK_CUR, SEEK_END
*/ */
bool Seek(goffset offset, int whence, Error &error); bool Seek(offset_type offset, int whence, Error &error);
/** /**
* Wrapper for Seek() which locks and unlocks the mutex; the * Wrapper for Seek() which locks and unlocks the mutex; the
* caller must not be holding it already. * caller must not be holding it already.
*/ */
bool LockSeek(goffset offset, int whence, Error &error); bool LockSeek(offset_type offset, int whence, Error &error);
/** /**
* Returns true if the stream has reached end-of-file. * Returns true if the stream has reached end-of-file.

View File

@ -36,6 +36,8 @@
#include "util/Error.hxx" #include "util/Error.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include <glib.h>
void void
playlist_print_uris(Client *client, const struct playlist *playlist) playlist_print_uris(Client *client, const struct playlist *playlist)
{ {

View File

@ -28,6 +28,8 @@
#include "Song.hxx" #include "Song.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include <glib.h>
enum playlist_result enum playlist_result
playlist_load_into_queue(const char *uri, SongEnumerator &e, playlist_load_into_queue(const char *uri, SongEnumerator &e,
unsigned start_index, unsigned end_index, unsigned start_index, unsigned end_index,

View File

@ -40,6 +40,8 @@
#include "system/FatalError.hxx" #include "system/FatalError.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <glib.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>

View File

@ -178,12 +178,12 @@ zzip_input_eof(struct input_stream *is)
{ {
ZzipInputStream *zis = (ZzipInputStream *)is; ZzipInputStream *zis = (ZzipInputStream *)is;
return (goffset)zzip_tell(zis->file) == is->size; return (InputPlugin::offset_type)zzip_tell(zis->file) == is->size;
} }
static bool static bool
zzip_input_seek(struct input_stream *is, zzip_input_seek(struct input_stream *is, InputPlugin::offset_type offset,
goffset offset, int whence, Error &error) int whence, Error &error)
{ {
ZzipInputStream *zis = (ZzipInputStream *)is; ZzipInputStream *zis = (ZzipInputStream *)is;
zzip_off_t ofs = zzip_seek(zis->file, offset, whence); zzip_off_t ofs = zzip_seek(zis->file, offset, whence);

View File

@ -63,7 +63,7 @@ dsdlib_read(struct decoder *decoder, struct input_stream *is,
*/ */
bool bool
dsdlib_skip_to(struct decoder *decoder, struct input_stream *is, dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
goffset offset) int64_t offset)
{ {
if (is->IsSeekable()) if (is->IsSeekable())
return is->Seek(offset, SEEK_SET, IgnoreError()); return is->Seek(offset, SEEK_SET, IgnoreError());
@ -74,7 +74,7 @@ dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
char buffer[8192]; char buffer[8192];
while (is->GetOffset() < offset) { while (is->GetOffset() < offset) {
size_t length = sizeof(buffer); size_t length = sizeof(buffer);
if (offset - is->GetOffset() < (goffset)length) if (offset - is->GetOffset() < (int64_t)length)
length = offset - is->GetOffset(); length = offset - is->GetOffset();
size_t nbytes = decoder_read(decoder, is, buffer, length); size_t nbytes = decoder_read(decoder, is, buffer, length);
@ -91,7 +91,7 @@ dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
*/ */
bool bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is, dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta) int64_t delta)
{ {
assert(delta >= 0); assert(delta >= 0);
@ -104,7 +104,7 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
char buffer[8192]; char buffer[8192];
while (delta > 0) { while (delta > 0) {
size_t length = sizeof(buffer); size_t length = sizeof(buffer);
if ((goffset)length > delta) if ((int64_t)length > delta)
length = delta; length = delta;
size_t nbytes = decoder_read(decoder, is, buffer, length); size_t nbytes = decoder_read(decoder, is, buffer, length);
@ -126,7 +126,7 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
void void
dsdlib_tag_id3(struct input_stream *is, dsdlib_tag_id3(struct input_stream *is,
const struct tag_handler *handler, const struct tag_handler *handler,
void *handler_ctx, goffset tagoffset) void *handler_ctx, int64_t tagoffset)
{ {
assert(tagoffset >= 0); assert(tagoffset >= 0);
@ -140,8 +140,8 @@ dsdlib_tag_id3(struct input_stream *is,
id3_length_t count; id3_length_t count;
/* Prevent broken files causing problems */ /* Prevent broken files causing problems */
const goffset size = is->GetSize(); const auto size = is->GetSize();
const goffset offset = is->GetOffset(); const auto offset = is->GetOffset();
if (offset >= size) if (offset >= size)
return; return;

View File

@ -21,8 +21,7 @@
#define MPD_DECODER_DSDLIB_HXX #define MPD_DECODER_DSDLIB_HXX
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <glib.h>
struct dsdlib_id { struct dsdlib_id {
char value[4]; char value[4];
@ -37,15 +36,15 @@ dsdlib_read(struct decoder *decoder, struct input_stream *is,
bool bool
dsdlib_skip_to(struct decoder *decoder, struct input_stream *is, dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
goffset offset); int64_t offset);
bool bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is, dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta); int64_t delta);
void void
dsdlib_tag_id3(struct input_stream *is, dsdlib_tag_id3(struct input_stream *is,
const struct tag_handler *handler, const struct tag_handler *handler,
void *handler_ctx, goffset tagoffset); void *handler_ctx, int64_t tagoffset);
#endif #endif

View File

@ -72,13 +72,13 @@ struct DsdiffMetaData {
bool bitreverse; bool bitreverse;
uint64_t chunk_size; uint64_t chunk_size;
#ifdef HAVE_ID3TAG #ifdef HAVE_ID3TAG
goffset id3_offset; input_stream::offset_type id3_offset;
uint64_t id3_size; uint64_t id3_size;
#endif #endif
/** offset for artist tag */ /** offset for artist tag */
goffset diar_offset; input_stream::offset_type diar_offset;
/** offset for title tag */ /** offset for title tag */
goffset diti_offset; input_stream::offset_type diti_offset;
}; };
static bool lsbitfirst; static bool lsbitfirst;
@ -123,14 +123,14 @@ dsdiff_read_payload(struct decoder *decoder, struct input_stream *is,
static bool static bool
dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is, dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
DsdiffMetaData *metadata, DsdiffMetaData *metadata,
goffset end_offset) input_stream::offset_type end_offset)
{ {
DsdiffChunkHeader header; DsdiffChunkHeader header;
while ((goffset)(is->GetOffset() + sizeof(header)) <= end_offset) { while ((input_stream::offset_type)(is->GetOffset() + sizeof(header)) <= end_offset) {
if (!dsdiff_read_chunk_header(decoder, is, &header)) if (!dsdiff_read_chunk_header(decoder, is, &header))
return false; return false;
goffset chunk_end_offset = is->GetOffset() input_stream::offset_type chunk_end_offset = is->GetOffset()
+ header.GetSize(); + header.GetSize();
if (chunk_end_offset > end_offset) if (chunk_end_offset > end_offset)
return false; return false;
@ -184,7 +184,7 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
const DsdiffChunkHeader *prop_header) const DsdiffChunkHeader *prop_header)
{ {
uint64_t prop_size = prop_header->GetSize(); uint64_t prop_size = prop_header->GetSize();
goffset end_offset = is->GetOffset() + prop_size; input_stream::offset_type end_offset = is->GetOffset() + prop_size;
struct dsdlib_id prop_id; struct dsdlib_id prop_id;
if (prop_size < sizeof(prop_id) || if (prop_size < sizeof(prop_id) ||
@ -201,7 +201,7 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
static void static void
dsdiff_handle_native_tag(struct input_stream *is, dsdiff_handle_native_tag(struct input_stream *is,
const struct tag_handler *handler, const struct tag_handler *handler,
void *handler_ctx, goffset tagoffset, void *handler_ctx, input_stream::offset_type tagoffset,
enum tag_type type) enum tag_type type)
{ {
if (!dsdlib_skip_to(nullptr, is, tagoffset)) if (!dsdlib_skip_to(nullptr, is, tagoffset))
@ -259,7 +259,7 @@ dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
/* Now process all the remaining chunk headers in the stream /* Now process all the remaining chunk headers in the stream
and record their position and size */ and record their position and size */
const goffset size = is->GetSize(); const auto size = is->GetSize();
while (is->GetOffset() < size) { while (is->GetOffset() < size) {
uint64_t chunk_size = chunk_header->GetSize(); uint64_t chunk_size = chunk_header->GetSize();
@ -351,8 +351,8 @@ dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
} else { } else {
/* ignore unknown chunk */ /* ignore unknown chunk */
const uint64_t chunk_size = chunk_header->GetSize(); const uint64_t chunk_size = chunk_header->GetSize();
goffset chunk_end_offset = is->GetOffset() input_stream::offset_type chunk_end_offset =
+ chunk_size; is->GetOffset() + chunk_size;
if (!dsdlib_skip_to(decoder, is, chunk_end_offset)) if (!dsdlib_skip_to(decoder, is, chunk_end_offset))
return false; return false;

View File

@ -47,7 +47,7 @@ struct DsfMetaData {
bool bitreverse; bool bitreverse;
uint64_t chunk_size; uint64_t chunk_size;
#ifdef HAVE_ID3TAG #ifdef HAVE_ID3TAG
goffset id3_offset; input_stream::offset_type id3_offset;
uint64_t id3_size; uint64_t id3_size;
#endif #endif
}; };
@ -176,7 +176,7 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
if (metadata_offset >= size) if (metadata_offset >= size)
metadata->id3_offset = 0; metadata->id3_offset = 0;
else else
metadata->id3_offset = (goffset) metadata_offset; metadata->id3_offset = (input_stream::offset_type)metadata_offset;
#endif #endif
/* check bits per sample format, determine if bitreverse is needed */ /* check bits per sample format, determine if bitreverse is needed */
metadata->bitreverse = dsf_fmt_chunk.bitssample == 1; metadata->bitreverse = dsf_fmt_chunk.bitssample == 1;

View File

@ -170,7 +170,7 @@ faad_song_duration(DecoderBuffer *buffer, struct input_stream *is)
size_t length; size_t length;
bool success; bool success;
const goffset size = is->GetSize(); const auto size = is->GetSize();
fileread = size >= 0 ? size : 0; fileread = size >= 0 ? size : 0;
decoder_buffer_fill(buffer); decoder_buffer_fill(buffer);

View File

@ -152,10 +152,10 @@ struct MadDecoder {
enum mp3_action DecodeNextFrame(); enum mp3_action DecodeNextFrame();
gcc_pure gcc_pure
goffset ThisFrameOffset() const; input_stream::offset_type ThisFrameOffset() const;
gcc_pure gcc_pure
goffset RestIncludingThisFrame() const; input_stream::offset_type RestIncludingThisFrame() const;
/** /**
* Attempt to calulcate the length of the song from filesize * Attempt to calulcate the length of the song from filesize
@ -776,10 +776,10 @@ mp3_frame_duration(const struct mad_frame *frame)
MAD_UNITS_MILLISECONDS) / 1000.0; MAD_UNITS_MILLISECONDS) / 1000.0;
} }
inline goffset inline input_stream::offset_type
MadDecoder::ThisFrameOffset() const MadDecoder::ThisFrameOffset() const
{ {
goffset offset = input_stream->GetOffset(); auto offset = input_stream->GetOffset();
if (stream.this_frame != nullptr) if (stream.this_frame != nullptr)
offset -= stream.bufend - stream.this_frame; offset -= stream.bufend - stream.this_frame;
@ -789,7 +789,7 @@ MadDecoder::ThisFrameOffset() const
return offset; return offset;
} }
inline goffset inline input_stream::offset_type
MadDecoder::RestIncludingThisFrame() const MadDecoder::RestIncludingThisFrame() const
{ {
return input_stream->GetSize() - ThisFrameOffset(); return input_stream->GetSize() - ThisFrameOffset();
@ -798,7 +798,7 @@ MadDecoder::RestIncludingThisFrame() const
inline void inline void
MadDecoder::FileSizeToSongLength() MadDecoder::FileSizeToSongLength()
{ {
goffset rest = RestIncludingThisFrame(); input_stream::offset_type rest = RestIncludingThisFrame();
if (rest > 0) { if (rest > 0) {
float frame_duration = mp3_frame_duration(&frame); float frame_duration = mp3_frame_duration(&frame);

View File

@ -36,12 +36,12 @@ static constexpr Domain modplug_domain("modplug");
static constexpr size_t MODPLUG_FRAME_SIZE = 4096; static constexpr size_t MODPLUG_FRAME_SIZE = 4096;
static constexpr size_t MODPLUG_PREALLOC_BLOCK = 256 * 1024; static constexpr size_t MODPLUG_PREALLOC_BLOCK = 256 * 1024;
static constexpr size_t MODPLUG_READ_BLOCK = 128 * 1024; static constexpr size_t MODPLUG_READ_BLOCK = 128 * 1024;
static constexpr goffset MODPLUG_FILE_LIMIT = 100 * 1024 * 1024; static constexpr input_stream::offset_type MODPLUG_FILE_LIMIT = 100 * 1024 * 1024;
static GByteArray * static GByteArray *
mod_loadfile(struct decoder *decoder, struct input_stream *is) mod_loadfile(struct decoder *decoder, struct input_stream *is)
{ {
const goffset size = is->GetSize(); const input_stream::offset_type size = is->GetSize();
if (size == 0) { if (size == 0) {
LogWarning(modplug_domain, "file is empty"); LogWarning(modplug_domain, "file is empty");
@ -77,7 +77,7 @@ mod_loadfile(struct decoder *decoder, struct input_stream *is)
return nullptr; return nullptr;
} }
if (goffset(bdatas->len + ret) > MODPLUG_FILE_LIMIT) { if (input_stream::offset_type(bdatas->len + ret) > MODPLUG_FILE_LIMIT) {
LogWarning(modplug_domain, "stream too large"); LogWarning(modplug_domain, "stream too large");
g_free(data); g_free(data);
g_byte_array_free(bdatas, TRUE); g_byte_array_free(bdatas, TRUE);

View File

@ -46,7 +46,7 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
const double time_to_size = audio_format.GetTimeToSize(); const double time_to_size = audio_format.GetTimeToSize();
float total_time = -1; float total_time = -1;
const goffset size = is->GetSize(); const auto size = is->GetSize();
if (size >= 0) if (size >= 0)
total_time = size / time_to_size; total_time = size / time_to_size;
@ -74,8 +74,8 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
buffer, nbytes, 0) buffer, nbytes, 0)
: decoder_get_command(decoder); : decoder_get_command(decoder);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
goffset offset = (goffset)(time_to_size * input_stream::offset_type offset(time_to_size *
decoder_seek_where(decoder)); decoder_seek_where(decoder));
Error error; Error error;
if (is->LockSeek(offset, SEEK_SET, error)) { if (is->LockSeek(offset, SEEK_SET, error)) {

View File

@ -250,7 +250,7 @@ input_cdio_open(const char *uri,
static bool static bool
input_cdio_seek(struct input_stream *is, input_cdio_seek(struct input_stream *is,
goffset offset, int whence, Error &error) InputPlugin::offset_type offset, int whence, Error &error)
{ {
CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is; CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is;

View File

@ -780,7 +780,7 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
if (c->icy.IsDefined()) if (c->icy.IsDefined())
copy_icy_tag(c); copy_icy_tag(c);
is->offset += (goffset)nbytes; is->offset += (InputPlugin::offset_type)nbytes;
if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) { if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) {
c->base.mutex.unlock(); c->base.mutex.unlock();
@ -977,7 +977,8 @@ input_curl_easy_init(struct input_curl *c, Error &error)
} }
static bool static bool
input_curl_seek(struct input_stream *is, goffset offset, int whence, input_curl_seek(struct input_stream *is, InputPlugin::offset_type offset,
int whence,
Error &error) Error &error)
{ {
struct input_curl *c = (struct input_curl *)is; struct input_curl *c = (struct input_curl *)is;
@ -1022,7 +1023,7 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
while (offset > is->offset && !c->buffers.empty()) { while (offset > is->offset && !c->buffers.empty()) {
auto &buffer = c->buffers.front(); auto &buffer = c->buffers.front();
size_t length = buffer.Available(); size_t length = buffer.Available();
if (offset - is->offset < (goffset)length) if (offset - is->offset < (InputPlugin::offset_type)length)
length = offset - is->offset; length = offset - is->offset;
const bool empty = !buffer.Consume(length); const bool empty = !buffer.Consume(length);

View File

@ -34,6 +34,8 @@ extern "C" {
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
} }
#include <glib.h>
struct FfmpegInputStream { struct FfmpegInputStream {
struct input_stream base; struct input_stream base;
@ -146,7 +148,8 @@ input_ffmpeg_eof(struct input_stream *is)
} }
static bool static bool
input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence, input_ffmpeg_seek(struct input_stream *is, InputPlugin::offset_type offset,
int whence,
Error &error) Error &error)
{ {
FfmpegInputStream *i = (FfmpegInputStream *)is; FfmpegInputStream *i = (FfmpegInputStream *)is;

View File

@ -97,12 +97,13 @@ input_file_open(const char *filename,
} }
static bool static bool
input_file_seek(struct input_stream *is, goffset offset, int whence, input_file_seek(struct input_stream *is, InputPlugin::offset_type offset,
int whence,
Error &error) Error &error)
{ {
FileInputStream *fis = (FileInputStream *)is; FileInputStream *fis = (FileInputStream *)is;
offset = (goffset)lseek(fis->fd, (off_t)offset, whence); offset = (InputPlugin::offset_type)lseek(fis->fd, (off_t)offset, whence);
if (offset < 0) { if (offset < 0) {
error.SetErrno("Failed to seek"); error.SetErrno("Failed to seek");
return false; return false;

View File

@ -164,7 +164,7 @@ input_rewind_read(struct input_stream *is, void *ptr, size_t size,
size_t nbytes = r->input->Read(ptr, size, error); size_t nbytes = r->input->Read(ptr, size, error);
if (r->input->offset > (goffset)sizeof(r->buffer)) if (r->input->offset > (InputPlugin::offset_type)sizeof(r->buffer))
/* disable buffering */ /* disable buffering */
r->tail = 0; r->tail = 0;
else if (r->tail == (size_t)is->offset) { else if (r->tail == (size_t)is->offset) {
@ -191,14 +191,16 @@ input_rewind_eof(struct input_stream *is)
} }
static bool static bool
input_rewind_seek(struct input_stream *is, goffset offset, int whence, input_rewind_seek(struct input_stream *is, InputPlugin::offset_type offset,
int whence,
Error &error) Error &error)
{ {
RewindInputStream *r = (RewindInputStream *)is; RewindInputStream *r = (RewindInputStream *)is;
assert(is->ready); assert(is->ready);
if (whence == SEEK_SET && r->tail > 0 && offset <= (goffset)r->tail) { if (whence == SEEK_SET && r->tail > 0 &&
offset <= (InputPlugin::offset_type)r->tail) {
/* buffered seek */ /* buffered seek */
assert(!r->ReadingFromBuffer() || assert(!r->ReadingFromBuffer() ||