在第一次调用时替换fopencookie的缓冲区:内部缓冲区?

时间:2018-03-15 15:52:30

标签: c glibc

我尝试使用FILE界面创建类似文件的对象 fopencookie的。

我的目标是拥有固定大小的缓冲区,并始终拥有前缀" HDR"在 每条印刷线的开头。

例如,如果用户代码用于

fprintf(fd, "hello there\n");
fprintf(fd, "how are you?\n");
fprintf(fd, "let's try again...\n");

我想打印

HDRhello there
HDRhow are you?
HDRlet's try again...

为了实现这一点,我做了一个小型原型,它使用了 fopencookie提供一个固定大小的缓冲区,移位尽可能多的字符 需要存储标题" HDR"。

struct blah
{
    FILE* fd;
    char* buffer;
};

void blah_init(struct blah* blah, size_t len)
{
    blah->buffer = calloc(len + 1, sizeof(char));
    blah->buffer[len] = 0;
    int hdrlen = snprintf(blah->buffer, len + 1, "HDR");
    cookie_io_functions_t cbacks = {
        .write = mywrite,
        .close = myclose,
    };
    blah->fd = fopencookie(blah, "w", cbacks);
    setvbuf(blah->fd, blah->buffer + hdrlen, _IOFBF, len - hdrlen);
}

为了看看我打印时会发生什么,我添加了mywrite的虚拟实现 和myclose

ssize_t mywrite(void* cookie, const char* buf, size_t len)
{
    struct blah* blah = (struct blah*) cookie;

    fprintf(stderr, "contains: \"%s\" (%p)\n", buf, buf);
    fprintf(stderr, "actually: \"%s\" (%p)\n", blah->buffer, blah->buffer);
    return len;
}

int myclose(void* cookie)
{
    free(((struct blah*)cookie)->buffer);
    return 0;
}

所以我希望contans:后跟用户打印的字符串 代码,actually:后跟相同的字符串,前缀为 报头中。

换句话说,鉴于此主要内容:

int main()
{
    struct blah blah;
    blah_init(&blah, 20);

    fputs("goat chee", blah.fd);
    fputs("se or ", blah.fd);
    fflush(blah.fd);
    fputs("goat soup", blah.fd);
    fclose(blah.fd);

    return 0;
}

我希望看到

contains: "goat chee" (x + 3)
actually: "HDRgoat chee" (x)
contains: "se or " (x + 3)
actually: "HDRse or " (x)
contains: "goat soup" (x + 3)
actually: "HDRgoat soup" (x)

虽然令人惊讶但我得到......

contains: "goat chee" (0x400a40)
actually: "HDR" (0x51ef040)
contains: "se or " (0x51ef043)
actually: "HDRse or " (0x51ef040)
contains: "goat soup" (0x51ef043)
actually: "HDRgoat soup" (0x51ef040)

所以,显然第一次调用我的写函数就是作弊和使用 一些内部缓冲区,而不是我提供的缓冲区。

这是正常行为吗?难道我做错了什么? glibc bug?

以下是完整的代码,如果有人想尝试的话。 我目前在Fedora 27下使用glibc 2.26运行它 (glibc-2.26-27.fc27.x86_64

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

struct blah
{
    FILE* fd;
    char* buffer;
};

ssize_t mywrite(void* cookie, const char* buf, size_t len)
{
    struct blah* blah = (struct blah*) cookie;

    fprintf(stderr, "contains: \"%s\" (%p)\n", buf, buf);
    fprintf(stderr, "actually: \"%s\" (%p)\n", blah->buffer, blah->buffer);
    return len;
}

int myclose(void* cookie)
{
    free(((struct blah*)cookie)->buffer);
    return 0;
}

void blah_init(struct blah* blah, size_t len)
{
    blah->buffer = calloc(len + 1, sizeof(char));
    blah->buffer[len] = 0;
    int hdrlen = snprintf(blah->buffer, len + 1, "HDR");
    cookie_io_functions_t cbacks = {
        .write = mywrite,
        .close = myclose,
    };
    blah->fd = fopencookie(blah, "w", cbacks);
    setvbuf(blah->fd, blah->buffer + hdrlen, _IOFBF, len - hdrlen);
}

int main()
{
    struct blah blah;
    blah_init(&blah, 20);

    fputs("goat chee", blah.fd);
    fputs("se or ", blah.fd);
    fflush(blah.fd);
    fputs("goat soup", blah.fd);
    fclose(blah.fd);

    return 0;
}

0 个答案:

没有答案