static sg_error sg_vector_clone_into_int(sg_vector **dest, const sg_vector *src){ /* we can assume that everything is correct from caller perspective */ size_t i; sg_vector *tmp = ((*dest)->used_count == src->used_count) ? *dest : sg_vector_resize(*dest, src->used_count); char const *src_data = VECTOR_DATA_CONST(src); char *dest_data = VECTOR_DATA(tmp); size_t item_size = src->info.item_size; assert(src->info.copy_fn); if( !tmp ) { RETURN_FROM_PREVIOUS_ERROR( "vector", sg_get_error() ); } for( i = 0; i < src->used_count; ++i ) { sg_error rc = src->info.copy_fn( src_data + i * item_size, dest_data + i * item_size ); if( SG_ERROR_NONE != rc ) { sg_vector_free( tmp ); *dest = NULL; return rc; } } *dest = tmp; return SG_ERROR_NONE; }
/** * Saves changed achievements for a character. * @param[in] char_id character identifier. * @param[out] cp pointer to loaded achievements. * @param[in] p pointer to map-sent character achievements. * @return number of achievements saved. */ static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) { StringBuf buf; int i = 0, rows = 0; nullpo_ret(cp); nullpo_ret(p); Assert_ret(char_id > 0); StrBuf->Init(&buf); StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) StrBuf->Printf(&buf, ", `obj_%d`", i); StrBuf->AppendStr(&buf, ") VALUES "); for (i = 0; i < VECTOR_LENGTH(*p); i++) { int j = 0; bool save = false; struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); if (j == VECTOR_LENGTH(*cp)) save = true; else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) save = true; if (save) { StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); StrBuf->AppendStr(&buf, ")"); rows++; } } if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { Sql_ShowDebug(inter->sql_handle); StrBuf->Destroy(&buf); // Destroy the buffer. return 0; } // Destroy the buffer. StrBuf->Destroy(&buf); if (rows) { ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); /* Sync with inter-db acheivements. */ VECTOR_CLEAR(*cp); VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); } return rows; }
static void sg_vector_init_new(struct sg_vector *vector, size_t new_count){ /* Initialise any new items. */ if (new_count > vector->used_count && vector->info.init_fn != NULL) { char *data = VECTOR_DATA(vector); size_t i; for( i = vector->used_count; i < new_count; ++i ){ vector->info.init_fn(data + i * vector->info.item_size); } } if (new_count > vector->used_count) vector->used_count = new_count; }
static void sg_vector_destroy_unused(struct sg_vector *vector, size_t new_count){ /* Destroy any now-unused items. * * Note that there's an assumption here that making the vector smaller * will never fail; if it did, then we would have destroyed items here * but not actually got rid of the vector pointing to them before the * error return.) */ if (new_count < vector->used_count && vector->info.destroy_fn != NULL) { char *data = VECTOR_DATA(vector); size_t i; i = vector->used_count; while( i > new_count ) { --i; vector->info.destroy_fn(data + i * vector->info.item_size); } } if (new_count < vector->used_count) vector->used_count = new_count; }
sg_error sg_vector_compute_diff(sg_vector **dest_vector_ptr, const sg_vector *cur_vector, const sg_vector *last_vector) { if( NULL == dest_vector_ptr ) { RETURN_WITH_SET_ERROR("vector", SG_ERROR_INVALID_ARGUMENT, "sg_vector_compute_diff(dest_vector_ptr)"); } if( cur_vector ) { sg_error rc = sg_vector_clone_into( dest_vector_ptr, cur_vector ); /* proves *dest, cur */ if( SG_ERROR_NONE != rc ) { RETURN_FROM_PREVIOUS_ERROR( "vector", rc ); } if( NULL == *dest_vector_ptr ) return SG_ERROR_NONE; assert( cur_vector->info.compute_diff_fn ); assert( cur_vector->info.compare_fn ); TRACE_LOG_FMT("vector", "computing vector diff between vector containing %lu items (cur) and vector containing %lu items (last)", cur_vector->used_count, last_vector ? last_vector->used_count : 0 ); if( last_vector && ( SG_ERROR_NONE == sg_prove_vector(last_vector) ) && ( SG_ERROR_NONE == sg_prove_vector_compat(cur_vector, last_vector ) ) ) { size_t i, item_size = last_vector->info.item_size; unsigned matched[(cur_vector->used_count / (8 * sizeof(unsigned))) + 1]; char *diff = VECTOR_DATA(*dest_vector_ptr); char const *last = VECTOR_DATA_CONST(last_vector); memset( matched, 0, sizeof(matched) ); for( i = 0; i < (*dest_vector_ptr)->used_count; ++i ) { size_t j; for( j = 0; j < last_vector->used_count; ++j ) { if( BIT_ISSET(matched, j) ) /* already matched? */ continue; if (last_vector->info.compare_fn(last + j * item_size, diff + i * item_size) == 0) { BIT_SET(matched, j); last_vector->info.compute_diff_fn(last + j * item_size, diff + i * item_size); } } if( j == last_vector->used_count ) { /* We have lost an item since last time - skip */ } } /* XXX append the items lost since last run? */ } } else { sg_vector_free(*dest_vector_ptr); *dest_vector_ptr = NULL; RETURN_WITH_SET_ERROR("vector", SG_ERROR_INVALID_ARGUMENT, "sg_vector_compute_diff(cur_vector)"); } return SG_ERROR_NONE; }