Skip to content

Instantly share code, notes, and snippets.

@brun0xff
Last active May 16, 2017 21:14
Show Gist options
  • Save brun0xff/42ec402563fc2bfe9123fea996b1de71 to your computer and use it in GitHub Desktop.
Save brun0xff/42ec402563fc2bfe9123fea996b1de71 to your computer and use it in GitHub Desktop.
#include <iostream>
#include "aloca.h"
using namespace std;
/*
Anda na lista de livres e checa se o bloco à esquerda do ~ponteiro ta livre,
retorna ponteiro pro node na lista de livres caso esteja livre, e retorna
NULL caso contrario
*/
node_t * meualoc::isFreeLeft(char * ponteiro)
{
if(free_list)
{
char * size_ptr = ponteiro;
size_ptr = size_ptr - OFFSET_BYTE; /* size é o primeiro cara no header bytes 0 e 1 */
unsigned short int size_value = getCharToUnsignedShortInt(size_ptr);
if(size_ptr == &(memoria[0]))
{
return NULL;
}
else
{
node_t * node = free_list;
char * low_addr = free_list->addr; /* vai buscar menor endereco que é um candidato a ser bloco livre pela esquerda */
/* Caminha na lista de livres para encontrar menor endereco da lista de livres -- em outras palavras o bloco mais a esquerda */
while(node)
{
if(node->addr < low_addr)
{
low_addr = node->addr;
}
node = node->next;
}
node = free_list;
node_t * block = NULL; /* Apontará para bloco livre esquerda caso ele exista, caso nao exista permanece NULL */
while(node)
{
if(node->addr < size_ptr and node->addr >= low_addr) /* Pega o bloco da esquerda mais perto que o bloco ponteiro */
{
low_addr = node->addr;
block = node;
}
node = node->next;
}
if(block)
{
/* Checa se esse bloco está imediatamente a esquerda de ponteiro */
if((block->addr + block->len) == size_ptr)
{
return block;
}
}
}
}
return NULL;
}
/*
Anda na lista de livres e checa se o bloco à direita do ~ponteiro ta livre,
retorna ponteiro pro node na lista de livres caso esteja livre, e retorna
NULL caso contrario
*/
node_t * meualoc::isFreeRight(char * ponteiro)
{
if(free_list){
char * size_ptr = ponteiro;
size_ptr = size_ptr - OFFSET_BYTE; /* size é o primeiro cara no header bytes 0 e 1 */
unsigned short int size_value = getCharToUnsignedShortInt(size_ptr);
char * right_addr = size_ptr + (size_value + OFFSET_BYTE);
/* Se tem um MAGIC NUMBER no bloco ao lado, o bloco ta ocupado */
//buscar na lista de free
node_t * node = free_list;
while(node)
{
if(node->addr == right_addr)
{
return node;
}
node = node->next;
}
}
return NULL;
}
/* retorna quantidade de bytes liberados */
int meualoc::coalescing(node_t * block_left, node_t * block_ptr, node_t * block_right)
{
if(block_left and block_ptr and block_right) /* Caso em que os tres blocos sao aglutinados */
{
printf("tamanho do novo bloco %d\n", block_left->len);
printf("addr do novo bloco livre %p\n", block_left->addr);
node_t * node = free_list;
printf("\n#######---Lista de Livres nesse momento-------######\n");
int cont = 0;
while(node)
{
printf("\n\tnode NUM: %d\n", cont);
printf("\tEndereco do bloco NODE: %p\n", node);
printf("\tNode-ADDR: %p\n", node->addr);
printf("\tNode-Len: %d\n", node->len);
printf("\tNode-Next: %p\n", node->next);
printf("\tNode-Prev: %p\n\n", node->prev);
node = node->next;
cont ++;
}
printf("\n#######---FIM -- Lista de Livres nesse-------######\n");
printf("\nBLOCO ESQUERDA\n");
printf("\tEndereco do bloco BLOCO ESQUERDA: %p\n", block_left);
printf("\tblock_left-ADDR: %p\n", block_left->addr);
printf("\tblock_left-Len: %d\n", block_left->len);
printf("\tblock_left-Next: %p\n", block_left->next);
printf("\tblock_left-Prev: %p\n\n", block_left->prev);
printf("FIMM -- BLOCO ESQUERDA\n");
printf("\nBLOCO MEIO\n");
printf("\tEndereco do bloco BLOCO ESQUERDA: %p\n", block_ptr);
printf("\tblock_ptr-ADDR: %p\n", block_ptr->addr);
printf("\tblock_ptr-Len: %d\n", block_ptr->len);
printf("\tblock_ptr-Next: %p\n", block_ptr->next);
printf("\tblock_ptr-Prev: %p\n\n", block_ptr->prev);
printf("FIMM -- BLOCO MEIO\n");
printf("\nBLOCO DIREITA\n");
printf("\tEndereco do bloco BLOCO ESQUERDA: %p\n", block_right);
printf("\tblock_right-ADDR: %p\n", block_right->addr);
printf("\tblock_right-Len: %d\n", block_right->len);
printf("\tblock_right-Next: %p\n", block_right->next);
printf("\tblock_right-Prev: %p\n\n", block_right->prev);
printf("FIMM -- BLOCO DIREITA\n");
if(block_left->addr < block_right->addr)
{
block_left->len = block_left->len + block_right->len + block_ptr->len;
len_fl = len_fl - 1;
free(block_ptr);
/* Se block_left é a cabeça da lista */
if(block_right->prev == NULL)
{
printf("cabeca\n");
free_list = block_right->next;
free_list->prev = NULL;
}
/* É o fim da lista */
else if(block_right->next == NULL)
{
printf("fim\n");
block_right->prev->next = NULL;
}
/* Ta no meio da lista */
else
{
printf("meio\n");
block_right->prev->next = block_right->next;
block_right->next->prev = block_right->prev;
}
free(block_right);
return block_left->len;
}
else /* if(block_left->addr > block_right->addr) */
{
block_right->len = block_right->len + block_left->len + block_ptr->len;
len_fl = len_fl - 1;
free(block_ptr);
/* Se block_left é a cabeça da lista */
if(block_left->prev == NULL)
{
printf("cabeca\n");
free_list = block_left->next;
free_list->prev = NULL;
}
/* É o fim da lista */
else if(block_left->next == NULL)
{
printf("fim\n");
block_left->prev->next = NULL;
}
/* Ta no meio da lista */
else
{
printf("meio\n");
block_left->prev->next = block_left->next;
block_left->next->prev = block_left->prev;
}
free(block_left);
return block_right->len;
}
}
else if(block_ptr and block_right) /* Caso em que o bloco do ponteiro é aglutinado com o bloco da direita que é livre */
{
printf("00222\n");
block_right->len = block_ptr->len + block_right->len;
block_right->addr = block_ptr->addr;
int num = block_ptr->len;
free(block_ptr);
return num;
}
else /* if(block_left and block_ptr) --- Caso em que o bloco da esquerda é livre e é aglutinado com o bloco do ponteiro */
{
printf("00111\n");
block_left->len = block_ptr->len + block_left->len;
int num = block_ptr->len;
free(block_ptr);
return num;
}
}
meualoc::meualoc(int tamanhoMemoria,int politicaMem)
{
memoria = (char *) malloc(sizeof(char) * tamanhoMemoria);
this->politicaMem = politicaMem;
this->tamanhoMemoria = tamanhoMemoria;
this->len_fl = 1;
/*
A lista de vazios sempre começa com pelo menos um elemento
vazio: A propria memoria;
*/
free_list = (node_t *) malloc(sizeof(node_t));
free_list->addr = &(memoria[0]);
free_list->len = tamanhoMemoria;
free_list->next = NULL;
free_list->prev = NULL;
}
char * meualoc::splitting(node_t * node, char *addr)
{
if(len_fl == 1)
{
/*
Atualiza lista de livres para NULL pois nao tem nenhum bloco livre
So entra aqui quando o bloco é a memoria inteira
*/
free_list = NULL;
free(node);
len_fl = 0;
return (addr + OFFSET_BYTE);
}
else
{
/* Será removido um bloco inteiro, e a lista de livres tem mais de um elemento, o elemento pode esta no inicio, meio ou fim da lista */
/* node é o cabeca da lista */
if(node->prev == NULL)
{
free_list = node->next;
free_list->prev = NULL;
}
/* node ta no fim da lista */
else if(node->next == NULL)
{
node->prev->next = NULL;
}
/* node ta no meio da lista */
else
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
len_fl = len_fl - 1;
free(node);
return (addr + OFFSET_BYTE);
}
}
char *meualoc::aloca(unsigned short int tamanho)
{
if(politicaMem == FIRSTFIT)
{
/* FIRSTFIT */
node_t * node = free_list; /* node caminha na lista de livres */
// printf("\n**** BLOCO ALOCA ****\n");
// printf("fl-node-addr: %p\n", node->addr);
// printf("fl-node-len: %d\n", node->len);
// printf("fl-node-next: %p\n", node->next);
// printf("fl-node-prev: %p\n", node->prev);
// printf("**** BLOCO ALOCA ****\n\n");
while(node)
{
char * addr = node->addr;
if( tamanho == ((node->len) - OFFSET_BYTE) )
{
setUnsignedShortIntToChar(node->addr, tamanho);
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
if(tamanho < (node->len)-OFFSET_BYTE) /* (4 header) -- o tamanho que se quer alocar é menor que o tamanho do bloco livre */
{
node->len = node->len - tamanho - OFFSET_BYTE;
/*
Se o tamanho do novo bloco nao couber pelo menos
1 byte + 4 bytes de header, esse pedaço nao serve pra nada
*/
/* bytes que sobrarão cabe um head + 1 byte */
if((node->len >= 0) and (node->len < (OFFSET_BYTE + 1)))
{
setUnsignedShortIntToChar(node->addr, node->len + tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
setUnsignedShortIntToChar(node->addr, tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
node->addr = node->addr + tamanho + OFFSET_BYTE;
/*
retorna posicao de memoria de tamanho passado pelo arg
somando com 4 bytes de header
*/
return (addr + OFFSET_BYTE);
}
node = node->next;
}
printf("Full memory size!\n");
return NULL; /* tamanho nao cabe na memoria, return null */
}
else if(politicaMem == BESTFIT)
{
/* BESTFIT */
node_t * node = free_list;
node_t * block = NULL;
int smaller = node->len;
while(node)
{
//condicao de parada pra alocar segundo BESTFIT
if( (node->len <= smaller) and (tamanho <= (node->len - OFFSET_BYTE)) )
{
smaller = node->len;
block = node;
}
node = node->next;
}
node = block;
/**/
if(node)
{
char * addr = node->addr;
if( tamanho == ((node->len) - OFFSET_BYTE) )
{
setUnsignedShortIntToChar(node->addr, tamanho);
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
if(tamanho < (node->len)-OFFSET_BYTE) /* (4 header) -- o tamanho que se quer alocar é menor que o tamanho do bloco livre */
{
node->len = node->len - tamanho - OFFSET_BYTE;
/*
Se o tamanho do novo bloco nao couber pelo menos
1 byte + 4 bytes de header, esse pedaço nao serve pra nada
*/
/* bytes que sobrarão cabe um head + 1 byte */
if((node->len >= 0) and (node->len < (OFFSET_BYTE + 1)))
{
setUnsignedShortIntToChar(node->addr, node->len + tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
setUnsignedShortIntToChar(node->addr, tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
node->addr = node->addr + tamanho + OFFSET_BYTE;
/*
retorna posicao de memoria de tamanho passado pelo arg
somando com 4 bytes de header
*/
return (addr + OFFSET_BYTE);
}
node = node->next;
}
printf("Full memory size!\n");
return NULL; /* tamanho nao cabe na memoria, return null */
/**/
}
else if(politicaMem == NEXTFIT)
{
/* NEXTFIT */
node_t * node = free_list;
node_t * block = NULL;
int bigger = node->len;
while(node)
{
//condicao de parada pra alocar segundo BESTFIT
if( (node->len >= bigger) and (tamanho <= (node->len - OFFSET_BYTE)) )
{
bigger = node->len;
block = node;
}
node = node->next;
}
node = block;
/**/
if(node)
{
char * addr = node->addr;
if( tamanho == ((node->len) - OFFSET_BYTE) )
{
setUnsignedShortIntToChar(node->addr, tamanho);
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
if(tamanho < (node->len)-OFFSET_BYTE) /* (4 header) -- o tamanho que se quer alocar é menor que o tamanho do bloco livre */
{
node->len = node->len - tamanho - OFFSET_BYTE;
/*
Se o tamanho do novo bloco nao couber pelo menos
1 byte + 4 bytes de header, esse pedaço nao serve pra nada
*/
/* bytes que sobrarão cabe um head + 1 byte */
if((node->len >= 0) and (node->len < (OFFSET_BYTE + 1)))
{
setUnsignedShortIntToChar(node->addr, node->len + tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
return splitting(node, addr);
}
setUnsignedShortIntToChar(node->addr, tamanho); /* sobre (node->len + tamanho) possivelmente é isso que acontece em libs como malloc, voce aloca x bytes e as vezes consegue usar x + i bytes, pois ele aloca um pedaço a mais, nesse caso aloco um pedaço a mais pois os bytes restantes nao caberiam um head + pelo menos um char (1 byte) */
setUnsignedShortIntToChar(node->addr+2, MAGIC_NUMBER);
node->addr = node->addr + tamanho + OFFSET_BYTE;
/*
retorna posicao de memoria de tamanho passado pelo arg
somando com 4 bytes de header
*/
return (addr + OFFSET_BYTE);
}
node = node->next;
}
printf("Full memory size!\n");
return NULL; /* tamanho nao cabe na memoria, return null */
}
printf("Politics has not one id valid!\n");
return NULL;
}
char *meualoc::verifica(char* ponteiro,int posicao)
{
if(ponteiro)
{
//header_t *hptr = (void *)ptr - sizeof(header_t);
char * size_ptr = ponteiro;
char * magic_ptr = ponteiro;
size_ptr = size_ptr - OFFSET_BYTE; /* size é o primeiro cara no header bytes 0 e 1 */
magic_ptr = magic_ptr - (OFFSET_BYTE/2); /* magic é o segundo cara no header bytes 2 e 3 */
unsigned short int size_value = getCharToUnsignedShortInt(size_ptr);
unsigned short int magic_value = getCharToUnsignedShortInt(magic_ptr);
if(magic_value == MAGIC_NUMBER)
{
/*
posicao < size_value pois entra apenas indices do bloco de memoria que
foram alocados, i.g: v[3] -> [0,1,2], posicao 3 false, mas posicao 2 true
*/
if(posicao < size_value)
{
//printf("Entrou\n");
/* return size_value; return que faz mais sentido */
return size_ptr; /* A atencao aqui é que esse cara precisa de 2 bytes pra guardar o short int */
}
}
}
//printf("Nao-Entrou\n");
return NULL;
}
int meualoc::libera(char* ponteiro)
{
if(ponteiro)
{
char * size_ptr = ponteiro;
char * magic_ptr = ponteiro;
size_ptr = size_ptr - OFFSET_BYTE; /* size é o primeiro cara no header bytes 0 e 1 */
magic_ptr = magic_ptr - (OFFSET_BYTE/2); /* magic é o segundo cara no header bytes 2 e 3 */
unsigned short int size_value = getCharToUnsignedShortInt(size_ptr);
unsigned short int magic_value = getCharToUnsignedShortInt(magic_ptr);
if(magic_value == MAGIC_NUMBER)
{
/*
No caso de liberar memoria, o magic number é alterado por
garantia e o novo bloco free é atualizado na lista de free
*/
setUnsignedShortIntToChar(magic_ptr, 0); /* 0 é um numero arbitrario != magic number */
/* Aqui é onde a mágica de fato acontece, deus queira */
node_t * ptr_block_left = isFreeLeft(ponteiro);
node_t * ptr_block_right = isFreeRight(ponteiro);
node_t * block_ptr = (node_t *)malloc(sizeof(node_t));
block_ptr->len = size_value + OFFSET_BYTE;
block_ptr->addr = ponteiro - OFFSET_BYTE;
block_ptr->next = NULL; /* apenas por controle, nao é necessario */
block_ptr->prev = NULL; /* apenas por controle, nao é necessario */
printf("----ptr_block_left %p\n", ptr_block_left);
printf("----block_ptr %p\n", block_ptr);
printf("----ptr_block_right %p\n", ptr_block_right);
if(ptr_block_left and ptr_block_right) /* Bloco a ser removido encontra-se no meio de dois blocos livres */
{
printf("01--if(ptr_block_left and ptr_block_right)\n");
int len_bytes = coalescing(ptr_block_left, block_ptr, ptr_block_right);
printf("len_bytes retorno da func aqu: %d\n", len_bytes);
printf("mais esse print antes do erro\n");
return len_bytes;
}
else if(ptr_block_right) /* Apenas bloco da direita ta livre, une bloco ponteiro + bloco da direita em um unico nodo na lista de livres */
{
printf("02--else if(ptr_block_right) \n");
int len_bytes = coalescing(NULL, block_ptr, ptr_block_right);
return len_bytes;
}
else if(ptr_block_left) /* Apenas o bloco da esquerda ta livre, une o bloco da esquerda + bloco ponteiro em um unico nodo na lista de livres */
{
printf("03--else if(ptr_block_left)\n");
int len_bytes = coalescing(ptr_block_left, block_ptr, NULL);
return len_bytes;
}
else /* Caso em que esquerda e direita sao blocos ocupados, dá free em ponteiro e add ele na lista de livres */
{
printf("04----ELSE--\n");
//cria um novo node pra lista do ponteiro que esta sendo removido
//add esse novo cara na lista de free
//incrementa o tamanho da lista de free
//retorna o len do novo bloco que ta sendo removido (o offset ja foi contado)
if(free_list == NULL)
{
free_list = block_ptr;
}
else
{
block_ptr->next = free_list;
free_list->prev = block_ptr;
free_list = block_ptr;
}
len_fl++;
return block_ptr->len;
}
}
}
return 0;
}
void meualoc::imprimeDados()
{
node_t * node = free_list; /* node caminha na lista de livres */
int higher_number = 0;
int sum = 0;
while(node)
{
if(node->len > higher_number)
{
higher_number = node->len;
}
sum += node->len;
node = node->next;
}
node = free_list;
while(node)
{
printf("\n==== NODO WHILE ====\n");
printf("node-addr: %p\n", node->addr);
printf("node-len: %d\n", node->len);
printf("node-next: %p\n", node->next);
printf("node-prev: %p\n", node->prev);
printf("==== NODO WHILE ====\n\n");
node = node->next;
}
float r = 0;
if(sum and len_fl){
r = float(sum/len_fl);
}
// printf("\nNº of blocks: %d\n", len_fl);
// printf("Larger free block: %d bytes\n", higher_number); /* menos 4 bytes de header */
// printf("Nº avg blocks free: %.2f\n\n", r);
}
meualoc::~meualoc()
{
free(memoria);
free(free_list);
}
void meualoc::setUnsignedShortIntToChar(char * mm, unsigned short int n)
{
/*
Pega 8 bits menos significativos de n e coloca nos menos
significativos de low_8bits preenchendo os mais significativos
de low_8bits com 0000 0000
*/
unsigned short low_8bits = n & 0xFF;
mm[0] = n & 0xFF;
/*
Pega os 8 bits mais significativos de n e coloca nos menos
significativos de high_8bits preenchendo os mais significativos
de high_8bits com 0000 0000
*/
unsigned short high_8bits = (n >> 8) & 0xFF;
mm[1] = (n >> 8) & 0xFF;
}
unsigned short int meualoc::getCharToUnsignedShortInt(char * mm)
{
/*
Le parte da memoria incialemente char interpretando-as como unsigned short int
*/
unsigned short int * num = (unsigned short int *)mm;
unsigned short int n = *num;
return n;
}
int main(int argc, const char ** argv)
{
meualoc obj(46, NEXTFIT);
// printf("**** MAIN ****\n");
// printf("Memoria Addr: %p\n", obj.memoria);
// printf("tamanhoMemoria: %d\n", obj.tamanhoMemoria);
// printf("politicaMem: %d\n", obj.politicaMem);
// printf("len_fl: %d\n", obj.len_fl);
// printf("**** MAIN ****\n");
char * p1 = obj.aloca(3);
printf("\np1: %p\n", p1);
//obj.imprimeDados();
char * p2 = obj.aloca(1);
printf("p2: %p\n", p2);
//obj.imprimeDados();
char * p3 = obj.aloca(1);
printf("p3: %p\n", p3);
//obj.imprimeDados();
char * p4 = obj.aloca(1);
printf("p4: %p\n", p4);
//obj.imprimeDados();
char * p5 = obj.aloca(1);
printf("p5: %p\n", p5);
//obj.imprimeDados();
char * p6 = obj.aloca(1);
printf("p6: %p\n", p6);
//obj.imprimeDados();
char * p7 = obj.aloca(4);
printf("p7: %p\n", p7);
//obj.imprimeDados();
char * p8 = obj.aloca(2);
printf("p8: %p\n", p8);
//obj.imprimeDados();
//obj.imprimeDados();
obj.libera(p1);
obj.libera(p3);
obj.libera(p5);
obj.libera(p7);
obj.libera(p2);
obj.imprimeDados();
// obj.imprimeDados();
// obj.imprimeDados();
// node_t * fl = obj.free_list;
// printf("\n**** BLOCO NODE ****\n");
// printf("fl-node-addr: %p\n", fl->addr);
// printf("fl-node-len: %d\n", fl->len);
// printf("fl-node-next: %p\n", fl->next);
// printf("fl-node-prev: %p\n", fl->prev);
// printf("**** BLOCO NODE ****\n\n");
// int ip1 = obj.libera(p1);
// int ip2 = obj.libera(p2);
// int ip3 = obj.libera(p3);
// int ip4 = obj.libera(p4);
// int ip5 = obj.libera(p5);
// int ip6 = obj.libera(p6);
// int ip7 = obj.libera(p7);
// int ip8 = obj.libera(p8);
/* for(int i=0; i<46; i++)
{
printf("memoria[%d]: %p\n",i, &(obj.memoria[i]));
}
printf("p1: %p\n", p1);
printf("p2: %p\n", p2);
printf("p3: %p\n", p3);
printf("p4: %p\n", p4);
printf("p5: %p\n", p5);
printf("p6: %p\n", p6);
printf("p7: %p\n", p7);
printf("p8: %p\n", p8);
*/
//obj.verifica(p1, 9);
//obj.imprimeDados();
//obj.verifica(p2);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment