/* * gaps.c * * Code to keep track of existing gaps, including allocation of new * gaplists, and calculation of needed gaps. Used in both file transfer and * message transfers * * Copyright (c) 2004 Todd MacDermid * */ #include #include #include /* * empty_gaplist resets a gaplist to its empty state, freeing all memory * used by nodes in the process. */ void empty_gaplist(struct gaplist *list) { struct gapnode *walk_node; struct gapnode *free_node; if(NULL == list) return; walk_node = list->gap_head; while(walk_node != NULL) { free_node = walk_node; walk_node = walk_node->next_gap; free(free_node); } list->num_gaps = 0; list->gap_head = NULL; list->gap_tail = NULL; } /* * parse_gaplist fills in a gaplist with the series of gaps provided * in the list. numgaps must be evenly divisible by 2, or else we've got * a very odd gap. We must also perform sanity checking on gaps, making * sure that each gap "makes sense." * * Returns 0 on success, -1 on failure. */ int parse_gaplist(cutlass_t *cut_handle, struct gaplist *list, uint8_t *gap_array, int numgaps) { struct gapnode *walk_node; struct gapnode *new_node; uint8_t *array_loc; uint32_t tmp_gap1; uint32_t tmp_gap2; int i; if((NULL == cut_handle) || (NULL == list) || (NULL == gap_array)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "parse_gaplist: Passed a NULL pointer\n"); return(-1); } walk_node = NULL; if(numgaps % 2) { cutlass_sysmsg(cut_handle, CUT_ERROR, "parse_gaplist: Odd number of gaps provided\n"); return(-1); } array_loc = gap_array; for(i = 0; i < numgaps; i += 2) { memcpy(&tmp_gap1, array_loc, 4); memcpy(&tmp_gap2, array_loc+4, 4); if(ntohl(tmp_gap1) >= ntohl(tmp_gap2)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "parse_gaplist: Gaps provided out of order\n"); return(-1); } array_loc += 8; } empty_gaplist(list); array_loc = gap_array; for(i = 0; i < numgaps; i+=2) { new_node = gapnode_init(cut_handle); if(new_node == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "parse_gaplist: gapnode_init() failed\n"); return(-1); } memcpy(&tmp_gap1, array_loc, 4); memcpy(&tmp_gap2, array_loc+4, 4); new_node->gap_start = ntohl(tmp_gap1); new_node->gap_end = ntohl(tmp_gap2); new_node->next_gap = NULL; if(walk_node == NULL) { list->gap_head = new_node; new_node->prev_gap = NULL; } else { new_node->prev_gap = walk_node; walk_node->next_gap = new_node; } list->gap_tail = new_node; walk_node = new_node; array_loc += 8; } list->num_gaps = numgaps; return(0); } /* * retrieve_gaplist fills the gap_array memory provided with a * network-ordered sorted list of existing gaps in the gaplist structure, * up to a maximum of numgaps, or the number of gaps actually in the * structure, whichever is less. Returns the number of gaps in the * returned gaplist, or -1 on failure */ int retrieve_gaplist(struct gaplist *list, uint32_t *gap_array, int numgaps) { struct gapnode *walk_node; uint32_t *array_loc; int i; if((NULL == list) || (NULL == gap_array)) { return(-1); } walk_node = list->gap_head; array_loc = gap_array; i = 0; while((walk_node != NULL) &&(i < numgaps)) { *array_loc = htonl(walk_node->gap_start); array_loc++; *array_loc = htonl(walk_node->gap_end); array_loc++; i += 2; walk_node = walk_node->next_gap; } return(i); } /* * total_gapsleft is a function that will return the total number of * bytes contained in all gaps. This can be used to determine how * much of the data is left to send (and therefore, how much has already * been sent). */ uint32_t total_gapsleft(struct gaplist *list) { struct gapnode *walk_node; uint32_t num_bytes = 0; if(NULL == list) return(0); walk_node = list->gap_head; while(walk_node != NULL) { num_bytes += (walk_node->gap_end - walk_node->gap_start); walk_node = walk_node->next_gap; } return(num_bytes); } /* * gaplist_first is a simple little function that returns the "starting * point" for the current gaplist. This is meant to be used in the case * of a location reset. */ uint32_t gaplist_first(struct channel *channel) { struct gapnode *first_node; first_node = channel->gap_list->gap_head; return(first_node->gap_start); } /* * gaplist_last is a simple little function that returns the "ending * point" for the current gaplist. This is meant to be used in the case * of a location reset. */ uint32_t gaplist_last(struct channel *channel) { struct gapnode *last_node; last_node = channel->gap_list->gap_tail; return(last_node->gap_end); } /* * gapnode_init creates memory for a struct gapnode and initializes it * to empty defaults. Returns a pointer to the object on success, or * NULL on failure. */ struct gapnode * gapnode_init(cutlass_t *cut_handle) { struct gapnode *new_node; new_node = calloc(1, sizeof(struct gapnode)); if(new_node == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "gapnode_init: Could not allocate memory\n"); return(NULL); } new_node->next_gap = NULL; new_node->prev_gap = NULL; return(new_node); } /* * gaplist_init allocates a struct gaplist, and initializes it to contain * one gap, from zero to the end number provided. Returns a pointer to * the new gaplist on success, or NULL on failure. */ struct gaplist * gaplist_init(cutlass_t *cut_handle, uint32_t end) { struct gaplist *new_list; struct gapnode *new_node; new_node = gapnode_init(cut_handle); if(new_node == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "gaplist_init: gapnode_init() failed\n"); return(NULL); } new_list = calloc(1, sizeof(struct gaplist)); if(new_list == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "gaplist_init: Could not allocate memory\n"); free(new_node); return(NULL); } new_node->gap_start = 0; new_node->gap_end = end; new_list->num_gaps = 1; new_list->gap_head = new_node; new_list->gap_tail = new_node; return(new_list); } /* * gaplist_bytes_find is used by the channel transport sending function * in order to locate the offset and number of bytes to be transmitted * to the remote host, given the current gaplist. Returns the number * of bytes to be transmitted on success, 0 if nothing is to be * written, and -1 on error */ int gaplist_bytes_find(cutlass_t *cut_handle, conn_t *conn, struct channel *channel, uint32_t *write_start) { struct gapnode *walk_node; int done; int gap_size; if((NULL == cut_handle) || (NULL == conn) || (NULL == channel) || (NULL == write_start)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "gaplist_bytes_find: Passed a NULL pointer\n"); return(-1); } done = 0; gap_size = 0; if(channel->end == CUT_FRONT_END) { walk_node = channel->gap_list->gap_head; while((walk_node != NULL) && (!done)) { if(walk_node->gap_end > channel->cur_offset) { if(walk_node->gap_start <= channel->cur_offset) { *write_start = channel->cur_offset; } else { *write_start = walk_node->gap_start; } gap_size = walk_node->gap_end - *write_start; if(gap_size > conn->mtu) { gap_size = conn->mtu; } done = 1; } walk_node = walk_node->next_gap; } } else { walk_node = channel->gap_list->gap_tail; while((walk_node != NULL) && (!done)) { if(walk_node->gap_start < channel->cur_offset) { *write_start = walk_node->gap_start; if(walk_node->gap_end <= channel->cur_offset) { gap_size = walk_node->gap_end - walk_node->gap_start; if(gap_size > conn->mtu) { *write_start = walk_node->gap_end - conn->mtu; gap_size = conn->mtu; } } else { gap_size = channel->cur_offset - walk_node->gap_start; if(gap_size > conn->mtu) { *write_start = channel->cur_offset - conn->mtu; gap_size = conn->mtu; } } done = 1; } walk_node = walk_node->prev_gap; } } cutlass_sysmsg(cut_handle, CUT_DEBUG, "gaplist_bytes_find: Want %d from offset %d (my off %d)\n", gap_size, *write_start, channel->cur_offset); return(gap_size); } /* * update_gaplist updates the existing gaplist with the range * of data filling in the gaps. 0 on success, 1 on the gaplist * being completely filled in, or -1 on some odd failure. */ int update_gaplist(cutlass_t *cut_handle, struct gaplist *list, uint32_t start, uint32_t end) { struct gapnode *free_node; struct gapnode *new_node; struct gapnode *walk_node; if((NULL == cut_handle) || (NULL == list)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "update_gaplist: Passed a NULL pointer\n"); return(-1); } if(start > end) { cutlass_sysmsg(cut_handle, CUT_ERROR, "update_gaplist: start greater than end\n"); return(-1); } if(list == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "update_gaplist: passed a NULL pointer\n"); return(-1); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "update_gaplist: start: %d end: %d\n", start, end); walk_node = list->gap_head; while(walk_node != NULL) { if((start < walk_node->gap_end) && (end > walk_node->gap_start)) { if((start > walk_node->gap_start) && (end < walk_node->gap_end)) { if(list->num_gaps < CUT_MAX_GAPS) { new_node = gapnode_init(cut_handle); if(new_node == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "update_gaplist: gapnode_init() failed\n"); return(-1); } new_node->prev_gap = walk_node; new_node->next_gap = walk_node->next_gap; walk_node->next_gap = new_node; if(new_node->next_gap != NULL) { new_node->next_gap->prev_gap = new_node; } else { list->gap_tail = new_node; } new_node->gap_start = end; new_node->gap_end = walk_node->gap_end; walk_node->gap_end = start; list->num_gaps++; } walk_node = walk_node->next_gap; } else if(start > walk_node->gap_start) { walk_node->gap_end = start; walk_node = walk_node->next_gap; } else if (end < walk_node->gap_end) { walk_node->gap_start = end; walk_node = walk_node->next_gap; } else { if(walk_node->next_gap != NULL) { walk_node->next_gap->prev_gap = walk_node->prev_gap; } else { list->gap_tail = walk_node->prev_gap; } if(walk_node->prev_gap != NULL) { walk_node->prev_gap->next_gap = walk_node->next_gap; } else { list->gap_head = walk_node->next_gap; } free_node = walk_node; walk_node = walk_node->next_gap; free(free_node); list->num_gaps--; } } else { walk_node = walk_node->next_gap; } } if(list->num_gaps == 0) { return(1); } return(0); } /* * destroy_gaplist frees all memory associated with a gaplist, including * all nodes and the list structure itself. No return value. */ void destroy_gaplist(struct gaplist *list) { if(list != NULL) { empty_gaplist(list); free(list); } }