Added assignment 2 (done)

This commit is contained in:
Claudio Maggioni (maggicl) 2019-11-17 23:27:15 +01:00
parent 0de237cd78
commit cb4cc2bbe2
8 changed files with 300 additions and 0 deletions

BIN
gallery/480x320.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

BIN
gallery/640x480.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
gallery/768x480.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

BIN
gallery/800x600.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

13
gallery/Makefile Normal file
View file

@ -0,0 +1,13 @@
all: test
gallery.o: gallery.c
$(CC) -Wall -c -o $@ $< $(CFLAGS)
test: gallery.o test.c
$(CC) -Wall -o $@ $^ $(CFLAGS)
tidy:
rm -f *.o
clean: tidy
rm -f test

206
gallery/gallery.c Normal file
View file

@ -0,0 +1,206 @@
// vim: set ts=8 sw=8 noet tw=80:
/**
* gallery.c - Claudio Maggioni
*
* External sources of information used:
* - man section 3 for the several library functions;
* - libpng.org documentation of PNG header an IHDR;
* - https://codereview.stackexchange.com/q/149751 for inspiration of ntohl
* function implementation.
*/
#include "gallery.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
struct image {
uint32_t width;
uint32_t height;
char * name;
};
struct image_ll {
struct image_ll* prev;
struct image first;
struct image_ll* next;
};
struct gallery {
size_t size;
struct image_ll* images;
};
static struct image_ll* find_by_name(struct gallery*, const char*);
static uint32_t ntohl (uint32_t);
static struct image_ll* ll_remove(struct gallery*, struct image_ll*);
const size_t INIT_CAP = 128;
const uint8_t HEADER[8] = { '\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' };
struct image_ll* find_by_name(struct gallery* g, const char* name) {
for (struct image_ll* i = g->images; i; i = i->next) {
if (i->first.name == name || !strcmp(i->first.name, name)) {
return i;
}
}
return NULL;
}
struct image_ll* ll_remove(struct gallery* g, struct image_ll* i) {
if (i->next) {
i->next->prev = i->prev;
}
if (i->prev) {
i->prev->next = i->next;
}
struct image_ll* n = i->next;
free(i);
g->size--;
if (g->size == 0) {
g->images = NULL;
}
return n;
}
// inspired by https://codereview.stackexchange.com/q/149751
uint32_t ntohl (const uint32_t nlong) {
uint8_t* data = (uint8_t*) &nlong;
return ((uint32_t) data[3]) |
((uint32_t) data[2] << 8) |
((uint32_t) data[1] << 16) |
((uint32_t) data[0] << 24);
}
struct gallery *gallery_new() {
struct gallery* g = malloc(sizeof(struct gallery));
if (!g) {
return NULL;
}
g->images = NULL;
g->size = 0;
return g;
}
void gallery_destroy(struct gallery *g) {
while (g->images) {
struct image_ll* i = g->images;
g->images = g->images->next;
free(i);
}
free(g);
}
/*
* remark: gallery_add(...) returns undocumented error code -3 when the malloc
* for the new image data struct fails
*/
int gallery_add(struct gallery *g, char* filename) {
if (find_by_name(g, filename)) {
return -1;
}
FILE* f = fopen(filename, "r");
if (!f) {
return -2;
}
uint8_t file_head[8];
if (fread(&file_head, 1, 8, f) != 8) {
fclose(f);
return -2;
}
if (memcmp(file_head, HEADER, sizeof(HEADER))) {
return -2;
}
if (fseek(f, 8, SEEK_CUR) == -1) {
perror("fseek failed");
fclose(f);
return -2;
}
struct image_ll* new = malloc(sizeof(struct image_ll));
if (!new) {
fclose(f);
return -3;
}
if (fread(&new->first, 8, 1, f) != 1) {
free(new);
fclose(f);
return -2;
}
new->first.width = ntohl(new->first.width);
new->first.height = ntohl(new->first.height);
new->first.name = filename;
new->prev = NULL;
new->next = g->images;
if (g->images) {
g->images->prev = new;
}
g->images = new;
g->size++;
return 1;
}
int gallery_rm(struct gallery* g, char* filename) {
struct image_ll* i = find_by_name(g, filename);
if (!i) {
return 0;
}
ll_remove(g, i);
return 1;
}
int gallery_count(struct gallery* g) {
return g->size;
}
int gallery_filter(struct gallery *g, int (*f)(char *name, int w, int h)) {
for (struct image_ll* i = g->images; i;) {
if (!f(i->first.name, i->first.width, i->first.height)) {
i = ll_remove(g, i);
} else {
i = i->next;
}
}
return g->size;
}
char* gallery_bestfit(struct gallery* g, int max_width, int max_height) {
struct image* best = NULL;
for (struct image_ll* i = g->images; i; i = i->next) {
uint32_t w = i->first.width;
uint32_t h = i->first.height;
if (w > max_width || h > max_height) {
continue;
}
if (!best || w * h > best->width * best->height) {
best = &i->first;
}
}
return best ? best->name : NULL;
}

43
gallery/gallery.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef __GALLERY_H__
#define __GALLERY_H__
struct gallery;
/* Constructor: allocates memory and returns a pointer to a new gallery.
*/
struct gallery *gallery_new();
/* Destructor: frees all the memory allocated to the gallery.
*/
void gallery_destroy(struct gallery *g);
/* Adds a PNG by filename to the gallery. If the filename is already in the
* gallery, returns -1; if the file does not exist, or if it is not a valid PNG
* format, returns -2; otherwise, returns 1 on success.
*/
int gallery_add(struct gallery *g, char *filename);
/* Removes a photo from the gallery. Returns 1 on successful removal, otherwise
* 0 if the photo is not in the gallery.
*/
int gallery_rm(struct gallery *g, char *filename);
/* Returns the number of photos in the gallery.
*/
int gallery_count(struct gallery *g);
/* Removes photos from the gallery that do not match a predicate function. The
* predicate is passed the filename, width and height of each photo. The
* predicate should return 1 to keep the photo, and 0 to remove the photo. The
* function returns the number of photos present after filtering.
*/
int gallery_filter(struct gallery *g,
int (*f)(char *filename, int width, int height));
/* Returns the filename of the photo that is the best fit for the dimensions
* width*height. If there are multiple best fits, any may be returned. If there
* are no photos that fit, returns NULL.
*/
char *gallery_bestfit(struct gallery *g, int max_width, int max_height);
#endif // __GALLERY_H__

38
gallery/test.c Normal file
View file

@ -0,0 +1,38 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "gallery.h"
int main() {
struct gallery *g = gallery_new();
assert(gallery_add(g, "480x320.png") == 1);
assert(gallery_count(g) == 1);
assert(gallery_add(g, "640x480.png") == 1);
assert(gallery_count(g) == 2);
assert(gallery_add(g, "480x320.png") == -1); // already exists
assert(gallery_count(g) == 2);
assert(gallery_add(g, "test.c") == -2); // invalid PNG format
assert(gallery_count(g) == 2);
assert(strcmp(gallery_bestfit(g, 640, 480), "640x480.png") == 0);
assert(strcmp(gallery_bestfit(g, 640, 481), "640x480.png") == 0);
assert(strcmp(gallery_bestfit(g, 640, 479), "480x320.png") == 0);
assert(gallery_bestfit(g, 0, 0) == NULL);
int myfilter(char *fn, int w, int h) { return w < 640; }
assert(gallery_filter(g, myfilter) == 1);
assert(gallery_count(g) == 1);
assert(gallery_rm(g, "480x320.png") == 1);
assert(gallery_count(g) == 0);
gallery_destroy(g);
return 0;
}