Skip to content

Commit

Permalink
[libc] Add implementations of ftell.
Browse files Browse the repository at this point in the history
Reviewed By: michaelrj, lntue

Differential Revision: https://reviews.llvm.org/D137395
  • Loading branch information
Siva Chandra Reddy committed Nov 7, 2022
1 parent d8233b5 commit 43e52ad
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 10 deletions.
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Expand Up @@ -385,6 +385,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.fread
libc.src.stdio.fread_unlocked
libc.src.stdio.fseek
libc.src.stdio.ftell
libc.src.stdio.funlockfile
libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked
Expand Down
1 change: 1 addition & 0 deletions libc/include/CMakeLists.txt
Expand Up @@ -162,6 +162,7 @@ add_gen_header(
.llvm-libc-macros.stdio_macros
.llvm-libc-types.cookie_io_functions_t
.llvm-libc-types.FILE
.llvm-libc-types.off_t
.llvm-libc-types.size_t
)

Expand Down
5 changes: 5 additions & 0 deletions libc/spec/stdc.td
Expand Up @@ -563,6 +563,11 @@ def StdC : StandardSpec<"stdc"> {
[ArgSpec<IntType>,
ArgSpec<FILEPtr>]
>,
FunctionSpec<
"ftell",
RetValSpec<LongType>,
[ArgSpec<FILEPtr>]
>,
FunctionSpec<
"putc",
RetValSpec<IntType>,
Expand Down
23 changes: 22 additions & 1 deletion libc/src/__support/File/file.cpp
Expand Up @@ -294,7 +294,28 @@ int File::seek(long offset, int whence) {
// Reset the eof flag as a seek might move the file positon to some place
// readable.
eof = false;
return platform_seek(this, offset, whence);
long platform_pos = platform_seek(this, offset, whence);
if (platform_pos >= 0)
return 0;
else
return -1;
}

long File::tell() {
FileLock lock(this);
long platform_offset;
if (eof)
platform_offset = platform_seek(this, 0, SEEK_END);
else
platform_offset = platform_seek(this, 0, SEEK_CUR);
if (platform_offset < 0)
return -1;
if (prev_op == FileOp::READ)
return platform_offset - (read_limit - pos);
else if (prev_op == FileOp::WRITE)
return platform_offset + pos;
else
return platform_offset;
}

int File::flush_unlocked() {
Expand Down
10 changes: 9 additions & 1 deletion libc/src/__support/File/file.h
Expand Up @@ -28,7 +28,9 @@ class File {

using WriteFunc = size_t(File *, const void *, size_t);
using ReadFunc = size_t(File *, void *, size_t);
using SeekFunc = int(File *, long, int);
// The SeekFunc is expected to return the current offset of the external
// file position indicator.
using SeekFunc = long(File *, long, int);
using CloseFunc = int(File *);
using FlushFunc = int(File *);

Expand Down Expand Up @@ -191,6 +193,8 @@ class File {

int seek(long offset, int whence);

long tell();

// If buffer has data written to it, flush it out. Does nothing if the
// buffer is currently being used as a read buffer.
int flush() {
Expand Down Expand Up @@ -283,6 +287,10 @@ class File {
// library.
File *openfile(const char *path, const char *mode);

// The platform_file library should implement it if it relevant for that
// platform.
int get_fileno(File *f);

extern File *stdin;
extern File *stdout;
extern File *stderr;
Expand Down
13 changes: 10 additions & 3 deletions libc/src/__support/File/linux_file.cpp
Expand Up @@ -22,7 +22,7 @@ namespace {

size_t write_func(File *, const void *, size_t);
size_t read_func(File *, void *, size_t);
int seek_func(File *, long, int);
long seek_func(File *, long, int);
int close_func(File *);
int flush_func(File *);

Expand Down Expand Up @@ -71,10 +71,12 @@ size_t read_func(File *f, void *buf, size_t size) {
return ret;
}

int seek_func(File *f, long offset, int whence) {
long seek_func(File *f, long offset, int whence) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
long result;
#ifdef SYS_lseek
long ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence);
result = ret;
#elif defined(SYS__llseek)
long result;
long ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32,
Expand All @@ -87,7 +89,7 @@ int seek_func(File *f, long offset, int whence) {
errno = -ret;
return -1;
}
return 0;
return result;
}

int close_func(File *f) {
Expand Down Expand Up @@ -164,6 +166,11 @@ File *openfile(const char *path, const char *mode) {
return file;
}

int get_fileno(File *f) {
auto *lf = reinterpret_cast<LinuxFile *>(f);
return lf->get_fd();
}

constexpr size_t STDIN_BUFFER_SIZE = 512;
char stdin_buffer[STDIN_BUFFER_SIZE];
static LinuxFile StdIn(0, stdin_buffer, STDIN_BUFFER_SIZE, _IOFBF, false,
Expand Down
12 changes: 12 additions & 0 deletions libc/src/stdio/CMakeLists.txt
Expand Up @@ -416,6 +416,18 @@ add_entrypoint_object(
libc.src.stdio.printf_core.vfprintf_internal
)

add_entrypoint_object(
ftell
SRCS
ftell.cpp
HDRS
ftell.h
DEPENDS
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
)

add_entrypoint_object(
remove
ALIAS
Expand Down
8 changes: 6 additions & 2 deletions libc/src/stdio/fopencookie.cpp
Expand Up @@ -39,14 +39,18 @@ size_t read_func(File *f, void *data, size_t size) {
reinterpret_cast<char *>(data), size);
}

int seek_func(File *f, long offset, int whence) {
long seek_func(File *f, long offset, int whence) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.seek == nullptr) {
errno = EINVAL;
return -1;
}
off64_t offset64 = offset;
return cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
if (result == 0)
return offset64;
else
return -1;
}

int close_func(File *f) {
Expand Down
20 changes: 20 additions & 0 deletions libc/src/stdio/ftell.cpp
@@ -0,0 +1,20 @@
//===-- Implementation of ftell -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/stdio/ftell.h"
#include "src/__support/File/file.h"

#include <stdio.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) {
return reinterpret_cast<__llvm_libc::File *>(stream)->tell();
}

} // namespace __llvm_libc
20 changes: 20 additions & 0 deletions libc/src/stdio/ftell.h
@@ -0,0 +1,20 @@
//===-- Implementation header of ftell --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_STDIO_FTELL_H
#define LLVM_LIBC_SRC_STDIO_FTELL_H

#include <stdio.h>

namespace __llvm_libc {

long ftell(::FILE *f);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_STDIO_FTELL_H
6 changes: 3 additions & 3 deletions libc/test/src/__support/File/file_test.cpp
Expand Up @@ -26,7 +26,7 @@ class StringFile : public __llvm_libc::File {

static size_t str_read(__llvm_libc::File *f, void *data, size_t len);
static size_t str_write(__llvm_libc::File *f, const void *data, size_t len);
static int str_seek(__llvm_libc::File *f, long offset, int whence);
static long str_seek(__llvm_libc::File *f, long offset, int whence);
static int str_close(__llvm_libc::File *f) { return 0; }
static int str_flush(__llvm_libc::File *f) { return 0; }

Expand Down Expand Up @@ -94,15 +94,15 @@ size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
return i;
}

int StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
long StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
StringFile *sf = static_cast<StringFile *>(f);
if (whence == SEEK_SET)
sf->pos = offset;
if (whence == SEEK_CUR)
sf->pos += offset;
if (whence == SEEK_END)
sf->pos = SIZE + offset;
return 0;
return sf->pos;
}

StringFile *new_string_file(char *buffer, size_t buflen, int bufmode,
Expand Down
19 changes: 19 additions & 0 deletions libc/test/src/stdio/CMakeLists.txt
Expand Up @@ -235,6 +235,25 @@ add_libc_unittest(
libc.src.stdio.fwrite
)

add_libc_unittest(
ftell_test
SUITE
libc_stdio_unittests
SRCS
ftell_test.cpp
DEPENDS
libc.include.errno
libc.include.stdio
libc.src.stdio.fclose
libc.src.stdio.fflush
libc.src.stdio.fopen
libc.src.stdio.fread
libc.src.stdio.fseek
libc.src.stdio.ftell
libc.src.stdio.fwrite
libc.src.stdio.setvbuf
)

add_subdirectory(printf_core)
add_subdirectory(scanf_core)
add_subdirectory(testdata)
63 changes: 63 additions & 0 deletions libc/test/src/stdio/ftell_test.cpp
@@ -0,0 +1,63 @@
//===-- Unittests for ftell -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/stdio/fclose.h"
#include "src/stdio/fflush.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fread.h"
#include "src/stdio/fseek.h"
#include "src/stdio/ftell.h"
#include "src/stdio/fwrite.h"
#include "src/stdio/setvbuf.h"
#include "utils/UnitTest/Test.h"

#include <stdio.h>

class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
protected:
void test_with_bufmode(int bufmode) {
constexpr char FILENAME[] = "testdata/ftell.test";
// We will set a special buffer to the file so that we guarantee buffering.
constexpr size_t BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
::FILE *file = __llvm_libc::fopen(FILENAME, "w+");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(__llvm_libc::setvbuf(file, buffer, bufmode, BUFFER_SIZE), 0);

// Include few '\n' chars to test when |bufmode| is _IOLBF.
constexpr char CONTENT[] = "12\n345\n6789";
constexpr size_t WRITE_SIZE = sizeof(CONTENT) - 1;
ASSERT_EQ(WRITE_SIZE, __llvm_libc::fwrite(CONTENT, 1, WRITE_SIZE, file));
// The above write should have buffered the written data and not have
// trasferred it to the underlying stream. But, ftell operation should
// still return the correct effective offset.
ASSERT_EQ(size_t(__llvm_libc::ftell(file)), WRITE_SIZE);

long offset = 5;
ASSERT_EQ(0, __llvm_libc::fseek(file, offset, SEEK_SET));
ASSERT_EQ(__llvm_libc::ftell(file), offset);
ASSERT_EQ(0, __llvm_libc::fseek(file, -offset, SEEK_END));
ASSERT_EQ(size_t(__llvm_libc::ftell(file)), size_t(WRITE_SIZE - offset));

ASSERT_EQ(0, __llvm_libc::fseek(file, 0, SEEK_SET));
constexpr size_t READ_SIZE = WRITE_SIZE / 2;
char data[READ_SIZE];
// Reading a small amount will actually read out much more data and
// buffer it. But, ftell should return the correct effective offset.
ASSERT_EQ(READ_SIZE, __llvm_libc::fread(data, 1, READ_SIZE, file));
ASSERT_EQ(size_t(__llvm_libc::ftell(file)), READ_SIZE);

ASSERT_EQ(0, __llvm_libc::fclose(file));
}
};

TEST_F(LlvmLibcFTellTest, TellWithFBF) { test_with_bufmode(_IOFBF); }

TEST_F(LlvmLibcFTellTest, TellWithNBF) { test_with_bufmode(_IONBF); }

TEST_F(LlvmLibcFTellTest, TellWithLBF) { test_with_bufmode(_IOLBF); }

0 comments on commit 43e52ad

Please sign in to comment.