|
@@ -425,15 +425,19 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|
int size = vmstate_size(opaque, field);
|
|
int size = vmstate_size(opaque, field);
|
|
uint64_t old_offset, written_bytes;
|
|
uint64_t old_offset, written_bytes;
|
|
JSONWriter *vmdesc_loop = vmdesc;
|
|
JSONWriter *vmdesc_loop = vmdesc;
|
|
|
|
+ bool is_prev_null = false;
|
|
|
|
|
|
trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
|
|
trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems);
|
|
if (field->flags & VMS_POINTER) {
|
|
if (field->flags & VMS_POINTER) {
|
|
first_elem = *(void **)first_elem;
|
|
first_elem = *(void **)first_elem;
|
|
assert(first_elem || !n_elems || !size);
|
|
assert(first_elem || !n_elems || !size);
|
|
}
|
|
}
|
|
|
|
+
|
|
for (i = 0; i < n_elems; i++) {
|
|
for (i = 0; i < n_elems; i++) {
|
|
void *curr_elem = first_elem + size * i;
|
|
void *curr_elem = first_elem + size * i;
|
|
const VMStateField *inner_field;
|
|
const VMStateField *inner_field;
|
|
|
|
+ bool is_null;
|
|
|
|
+ int max_elems = n_elems - i;
|
|
|
|
|
|
old_offset = qemu_file_transferred(f);
|
|
old_offset = qemu_file_transferred(f);
|
|
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
|
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
|
@@ -448,12 +452,39 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|
* not follow.
|
|
* not follow.
|
|
*/
|
|
*/
|
|
inner_field = vmsd_create_fake_nullptr_field(field);
|
|
inner_field = vmsd_create_fake_nullptr_field(field);
|
|
|
|
+ is_null = true;
|
|
} else {
|
|
} else {
|
|
inner_field = field;
|
|
inner_field = field;
|
|
|
|
+ is_null = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Due to the fake nullptr handling above, if there's mixed
|
|
|
|
+ * null/non-null data, it doesn't make sense to emit a
|
|
|
|
+ * compressed array representation spanning the entire array
|
|
|
|
+ * because the field types will be different (e.g. struct
|
|
|
|
+ * vs. nullptr). Search ahead for the next null/non-null element
|
|
|
|
+ * and start a new compressed array if found.
|
|
|
|
+ */
|
|
|
|
+ if (field->flags & VMS_ARRAY_OF_POINTER &&
|
|
|
|
+ is_null != is_prev_null) {
|
|
|
|
+
|
|
|
|
+ is_prev_null = is_null;
|
|
|
|
+ vmdesc_loop = vmdesc;
|
|
|
|
+
|
|
|
|
+ for (int j = i + 1; j < n_elems; j++) {
|
|
|
|
+ void *elem = *(void **)(first_elem + size * j);
|
|
|
|
+ bool elem_is_null = !elem && size;
|
|
|
|
+
|
|
|
|
+ if (is_null != elem_is_null) {
|
|
|
|
+ max_elems = j - i;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field,
|
|
vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field,
|
|
- i, n_elems);
|
|
|
|
|
|
+ i, max_elems);
|
|
|
|
|
|
if (inner_field->flags & VMS_STRUCT) {
|
|
if (inner_field->flags & VMS_STRUCT) {
|
|
ret = vmstate_save_state(f, inner_field->vmsd,
|
|
ret = vmstate_save_state(f, inner_field->vmsd,
|