void gf_fs_print_stats(GF_FilterSession *fsess)
{
	u64 run_time=0, active_time=0, nb_tasks=0, nb_filters=0;
	u32 i, count;

	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\n"));
	gf_mx_p(fsess->filters_mx);

	count=gf_list_count(fsess->filters);
	for (i=0; i<count; i++) {
		GF_Filter *f = gf_list_get(fsess->filters, i);
		if (f->multi_sink_target) continue;
		nb_filters++;
	}

	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Filter stats - %d filters\n", nb_filters));
	for (i=0; i<count; i++) {
		u32 k, ipids, opids;
		GF_Filter *f = gf_list_get(fsess->filters, i);
		if (f->multi_sink_target) continue;

		gf_mx_p(f->tasks_mx);

		ipids = f->num_input_pids;
		opids = f->num_output_pids;
		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\tFilter "));
		print_filter_name(f, GF_FALSE, GF_FALSE);
		GF_LOG(GF_LOG_INFO, GF_LOG_APP, (" : %d input pids %d output pids "LLU" tasks "LLU" us process time\n", ipids, opids, f->nb_tasks_done, f->time_process));

		if (ipids) {
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t"LLU" packets processed "LLU" bytes processed", f->nb_pck_processed, f->nb_bytes_processed));
			if (f->time_process) {
				GF_LOG(GF_LOG_INFO, GF_LOG_APP, (" (%g pck/sec %g mbps)", (Double) f->nb_pck_processed*1000000/f->time_process, (Double) f->nb_bytes_processed*8/f->time_process));
			}
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\n"));
		}
		if (opids) {
			if (f->nb_hw_pck_sent) {
				GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t"LLU" hardware frames sent", f->nb_hw_pck_sent));
				if (f->time_process) {
					GF_LOG(GF_LOG_INFO, GF_LOG_APP, (" (%g pck/sec)", (Double) f->nb_hw_pck_sent*1000000/f->time_process));
				}

			} else if (f->nb_pck_sent) {
				GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t"LLU" packets sent "LLU" bytes sent", f->nb_pck_sent, f->nb_bytes_sent));
				if (f->time_process) {
					GF_LOG(GF_LOG_INFO, GF_LOG_APP, (" (%g pck/sec %g mbps)", (Double) f->nb_pck_sent*1000000/f->time_process, (Double) f->nb_bytes_sent*8/f->time_process));
				}
			}
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\n"));
		}

		for (k=0; k<ipids; k++) {
			GF_FilterPidInst *pid = gf_list_get(f->input_pids, k);
			if (!pid->pid) continue;
			if (pid->requires_full_data_block && (pid->nb_reagg_pck != pid->pid->nb_pck_sent) ) {
				GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t* input PID %s: %d frames (%d packets) received\n", pid->pid->name, pid->nb_reagg_pck, pid->pid->nb_pck_sent));
			} else {
				GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t* input PID %s: %d packets received\n", pid->pid->name, pid->pid->nb_pck_sent));
			}
		}
#ifndef GPAC_DISABLE_LOG
		for (k=0; k<opids; k++) {
			GF_FilterPid *pid = gf_list_get(f->output_pids, k);
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t* output PID %s: %d packets sent\n", pid->name, pid->nb_pck_sent));
		}
		if (f->nb_errors) {
			GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\t\t%d errors while processing\n", f->nb_errors));
		}
#endif

		gf_mx_v(f->tasks_mx);
	}
	gf_mx_v(fsess->filters_mx);

	count=gf_list_count(fsess->threads);
	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Session stats - threads %d\n", 1+count));

	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\tThread %u: run_time "LLU" us active_time "LLU" us nb_tasks "LLU"\n", 1, fsess->main_th.run_time, fsess->main_th.active_time, fsess->main_th.nb_tasks));

	run_time+=fsess->main_th.run_time;
	active_time+=fsess->main_th.active_time;
	nb_tasks+=fsess->main_th.nb_tasks;

	for (i=0; i<count; i++) {
		GF_SessionThread *s = gf_list_get(fsess->threads, i);

		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\tThread %u: run_time "LLU" us active_time "LLU" us nb_tasks "LLU"\n", i+2, s->run_time, s->active_time, s->nb_tasks));

		run_time+=s->run_time;
		active_time+=s->active_time;
		nb_tasks+=s->nb_tasks;
	}
	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\nTotal: run_time "LLU" us active_time "LLU" us nb_tasks "LLU"\n", run_time, active_time, nb_tasks));
}


Bool gf_fs_forward_gf_event(GF_FilterSession *fsess, GF_Event *evt, Bool consumed, Bool skip_user)
{
	if (!fsess || fsess->in_final_flush) return GF_FALSE;

	if (fsess->event_listeners) {
		GF_FSEventListener *el;
		u32 i=0;

		gf_mx_p(fsess->evt_mx);
		fsess->in_event_listener ++;
		gf_mx_v(fsess->evt_mx);
		while ((el = gf_list_enum(fsess->event_listeners, &i))) {
			if (el->on_event(el->udta, evt, consumed)) {
				fsess->in_event_listener --;
				return GF_TRUE;
			}
		}
		fsess->in_event_listener --;
	}

	if (!skip_user && !consumed && fsess->ui_event_proc) {
		Bool res;
		res = gf_fs_ui_event(fsess, evt);
		return res;
	}
	return GF_FALSE;
}


void gf_fs_remove_filter_register(GF_FilterSession *session, GF_FilterRegister *freg)
{
	gf_list_del_item(session->registry, freg);
	gf_filter_sess_reset_graph(session, freg);
}


GF_FilterArgs *gf_filter_get_args(GF_Filter *filter)
{
	return filter ? filter->instance_args : NULL;
}


static Bool fs_default_event_proc(void *ptr, GF_Event *evt)
{
	GF_FilterSession *fs = (GF_FilterSession *)ptr;
	if (evt->type==GF_EVENT_QUIT) {
		gf_fs_abort(fs, GF_FS_FLUSH_FAST);
	}
	if (evt->type==GF_EVENT_MESSAGE) {
		if (evt->message.error) {
			if (evt->message.service) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Service %s %s: %s\n", evt->message.service, evt->message.message, gf_error_to_string(evt->message.error) ))
			} else {
				GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("%s: %s\n", evt->message.message, gf_error_to_string(evt->message.error) ))
			}
		}
	}

#ifdef GPAC_HAS_QJS
	if (fs->on_evt_task)
		return jsfs_on_event(fs, evt);
#endif
	return 0;
}


GF_Err gf_filter_set_process_event_ckb(GF_Filter *filter, Bool (*process_event_cbk)(GF_Filter *filter, const GF_FilterEvent *evt) )
{
	if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
		return GF_BAD_PARAM;
	}
	((GF_FilterRegister *) filter->freg)->process_event = process_event_cbk;
	return GF_OK;
}


GF_FilterPid *gf_filter_get_opid(GF_Filter *filter, u32 idx)
{
	return gf_list_get(filter->output_pids, idx);
}


void gf_filter_pid_init_play_event(GF_FilterPid *pid, GF_FilterEvent *evt, Double start, Double speed, const char *log_name)
{
	u32 pmode = GF_PLAYBACK_MODE_NONE;
	const GF_PropertyValue *p;
	Bool was_end = GF_FALSE;
	memset(evt, 0, sizeof(GF_FilterEvent));
	evt->base.type = GF_FEVT_PLAY;
	evt->base.on_pid = pid;

	evt->play.speed = 1.0;

	if ((speed<0) && !start) start = -1.0;

	p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_PLAYBACK_MODE);
	if (p) pmode = p->value.uint;

	evt->play.start_range = start;
	if (start<0) {
		was_end = GF_TRUE;
		p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_DURATION);
		if (p && p->value.lfrac.den) {
			evt->play.start_range *= -100;
			evt->play.start_range *= p->value.lfrac.num;
			evt->play.start_range /= 100 * p->value.lfrac.den;
		}
	}
	switch (pmode) {
	case GF_PLAYBACK_MODE_NONE:
		evt->play.start_range = 0;
		if (start) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support seek, ignoring start directive\n", log_name));
		}
		break;
	case GF_PLAYBACK_MODE_SEEK:
		if (speed != 1.0) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support speed, ignoring speed directive\n", log_name));
		}
		break;
	case GF_PLAYBACK_MODE_FASTFORWARD:
		if (speed<0) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support negative speed, ignoring speed directive\n", log_name));
			if (was_end) evt->play.start_range = 0;
		} else {
			evt->play.speed = speed;
		}
		break;
	default:
		evt->play.speed = speed;
		break;
	}
}


static GF_Err set_chapter_track(GF_ISOFile *file, u32 track, u32 chapter_ref_trak)
{
	u64 ref_duration, chap_duration;
	Double scale;
	GF_Err e;

	e = gf_isom_set_track_reference(file, chapter_ref_trak, GF_ISOM_REF_CHAP, gf_isom_get_track_id(file, track) );
	if (e) return e;
	e = gf_isom_set_track_enabled(file, track, GF_FALSE);
	if (e) return e;

	ref_duration = gf_isom_get_media_duration(file, chapter_ref_trak);
	chap_duration = gf_isom_get_media_duration(file, track);
	scale = (Double) (s64) gf_isom_get_media_timescale(file, track);
	scale /= gf_isom_get_media_timescale(file, chapter_ref_trak);
	ref_duration = (u64) (ref_duration * scale);

	if (chap_duration < ref_duration) {
		chap_duration -= gf_isom_get_sample_duration(file, track, gf_isom_get_sample_count(file, track));
		chap_duration = ref_duration - chap_duration;
		e = gf_isom_set_last_sample_duration(file, track, (u32) chap_duration);
		if (e) return e;
	}
#ifdef GPAC_ENABLE_COVERAGE
	if (gf_sys_is_cov_mode()) {
		u8 NbBits;
		u32 switchGroupID, nb_crit, size, reserved;
		u8 priority;
		Bool discardable;
		GF_AudioChannelLayout layout;
		gf_isom_get_sample_size(file, track, 1);
		gf_isom_get_sample_dts(file, track, 1);
		gf_isom_get_sample_from_dts(file, track, 0);
		gf_isom_set_sample_padding(file, track, 0);
		gf_isom_has_padding_bits(file, track);
		gf_isom_get_sample_padding_bits(file, track, 1, &NbBits);
		gf_isom_keep_utc_times(file, 1);
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
		gf_isom_set_single_moof_mode(file, GF_TRUE);
		gf_isom_reset_sample_count(NULL);
		gf_isom_set_traf_mss_timeext(NULL, 0, 0, 0);
		gf_isom_get_next_moof_number(NULL);
		gf_isom_set_fragment_reference_time(NULL, 0, 0, 0);
#endif
		//this one is not tested in master due to old-arch compat, to remove when we enable tests without old-arch
		gf_isom_get_audio_layout(file, track, 1, &layout);

		gf_isom_get_track_switch_parameter(file, track, 1, &switchGroupID, &nb_crit);
		gf_isom_sample_has_subsamples(file, track, 1, 0);
		gf_isom_sample_get_subsample(file, track, 1, 0, 1, &size, &priority, &reserved, &discardable);
#ifndef GPAC_DISABLE_ISOM_HINTING
		gf_isom_hint_blank_data(NULL, 0, 0);
		gf_isom_hint_sample_description_data(NULL, 0, 0, 1, 0, 0, 0);
		gf_isom_get_payt_info(NULL, 0, 0, NULL);
#endif
		gf_isom_estimate_size(file);

	}
#endif
	return GF_OK;
}


GF_Err gf_filter_set_rt_udta(GF_Filter *filter, void *udta)
{
	if (!filter) return GF_BAD_PARAM;
	filter->rt_udta = udta;
	return GF_OK;
}


static Bool session_should_abort(GF_FilterSession *fs)
{
	if (fs->run_status<GF_OK) return GF_TRUE;
	if (!fs->run_status) return GF_FALSE;
	return fs->in_final_flush;
}


void gf_filter_setup_failure(GF_Filter *filter, GF_Err reason)
{

	if (filter->in_connect_err) {
		filter->in_connect_err = reason;
		return;
	}

	//filter was already connected, trigger removal of all pid instances
	if (filter->num_input_pids) {
		gf_filter_reset_pending_packets(filter);
		filter->removed = GF_TRUE;
		gf_mx_p(filter->tasks_mx);
		while (filter->num_input_pids) {
			GF_FilterPidInst *pidinst = gf_list_get(filter->input_pids, 0);
			GF_Filter *sfilter = pidinst->pid->filter;
			gf_list_del_item(filter->input_pids, pidinst);
			pidinst->filter = NULL;
			filter->num_input_pids = gf_list_count(filter->input_pids);
			if (!filter->num_input_pids)
				filter->single_source = NULL;

			//post a pid_delete task to also trigger removal of the filter if needed
			gf_fs_post_task(filter->session, gf_filter_pid_inst_delete_task, sfilter, pidinst->pid, "pid_inst_delete", pidinst);
		}
		gf_mx_v(filter->tasks_mx);

		filter->session->last_connect_error = reason;
		return;
	}

	//don't accept twice a notif
	if (filter->setup_notified) return;
	filter->setup_notified = GF_TRUE;

	gf_filter_notification_failure(filter, reason, GF_TRUE);
}


GF_Filter *gf_filter_connect_source(GF_Filter *filter, const char *url, const char *parent_url, Bool inherit_args, GF_Err *err)
{
	GF_Filter *filter_src;
	const char *args;
	char *full_args = NULL;
	if (!filter) {
		if (err) *err = GF_BAD_PARAM;
		return NULL;
	}
	args = inherit_args ? gf_filter_get_dst_args(filter) : NULL;
	if (args) {
		char *rem_opts[] = {"FID", "SID", "N", "clone", NULL};
		char szSep[10];
		char *loc_args;
		u32 opt_idx;
		u32 dst_offset = 0;
		u32 len = (u32) strlen(args);
		sprintf(szSep, "%cgfloc%c", filter->session->sep_args, filter->session->sep_args);
		loc_args = strstr(args, szSep);
		if (loc_args) {
			len = (u32) (ptrdiff_t) (loc_args - args);
		}
		if (len) {
			gf_dynstrcat(&full_args, url, NULL);
			sprintf(szSep, "%cgpac%c", filter->session->sep_args, filter->session->sep_args);
			if ((filter->session->sep_args==':') && strstr(url, "://") && !strstr(url, szSep)) {
				gf_dynstrcat(&full_args, szSep, NULL);
			} else {
				sprintf(szSep, "%c", filter->session->sep_args);
				gf_dynstrcat(&full_args, szSep, NULL);
			}
			if (full_args)
				dst_offset = (u32) strlen(full_args);

			gf_dynstrcat(&full_args, args, NULL);
			sprintf(szSep, "%cgfloc%c", filter->session->sep_args, filter->session->sep_args);
			loc_args = strstr(full_args, "gfloc");
			if (loc_args) loc_args[0] = 0;

			//remove all internal options FIS, SID, N
			opt_idx = 0;
			while (rem_opts[opt_idx]) {
				sprintf(szSep, "%c%s%c", filter->session->sep_args, rem_opts[opt_idx], filter->session->sep_name);
				loc_args = strstr(full_args + dst_offset, szSep);
				if (loc_args) {
					char *sep = strchr(loc_args+1, filter->session->sep_args);
					if (sep) memmove(loc_args, sep, strlen(sep)+1);
					else loc_args[0] = 0;
				}
				opt_idx++;
			}
			url = full_args;
		}
	}

	filter_src = gf_fs_load_source_dest_internal(filter->session, url, NULL, parent_url, err, NULL, filter, GF_TRUE, GF_TRUE, NULL);
	if (full_args) gf_free(full_args);

	if (!filter_src) return NULL;

	gf_mx_p(filter->tasks_mx);
	if (!filter->source_filters)
		filter->source_filters = gf_list_new();
	gf_list_add(filter->source_filters, filter_src);
	gf_mx_v(filter->tasks_mx);
	return filter_src;
}


storageVolWipe(virStorageVolPtr vol,
               unsigned int flags)
{
    return storageVolWipePattern(vol, VIR_STORAGE_VOL_WIPE_ALG_ZERO, flags);
}


storageStateInitialize(bool privileged,
                       const char *root,
                       virStateInhibitCallback callback G_GNUC_UNUSED,
                       void *opaque G_GNUC_UNUSED)
{
    g_autofree char *configdir = NULL;
    g_autofree char *rundir = NULL;
    bool autostart = true;

    if (root != NULL) {
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("Driver does not support embedded mode"));
        return -1;
    }

    driver = g_new0(virStorageDriverState, 1);

    driver->lockFD = -1;
    if (virMutexInit(&driver->lock) < 0) {
        VIR_FREE(driver);
        return VIR_DRV_STATE_INIT_ERROR;
    }
    storageDriverLock();

    if (!(driver->pools = virStoragePoolObjListNew()))
        goto error;

    if (privileged) {
        driver->configDir = g_strdup(SYSCONFDIR "/libvirt/storage");
        driver->autostartDir = g_strdup(SYSCONFDIR "/libvirt/storage/autostart");
        driver->stateDir = g_strdup(RUNSTATEDIR "/libvirt/storage");
    } else {
        configdir = virGetUserConfigDirectory();
        rundir = virGetUserRuntimeDirectory();

        driver->configDir = g_strdup_printf("%s/storage", configdir);
        driver->autostartDir = g_strdup_printf("%s/storage/autostart", configdir);
        driver->stateDir = g_strdup_printf("%s/storage/run", rundir);
    }
    driver->privileged = privileged;

    if (g_mkdir_with_parents(driver->stateDir, 0777) < 0) {
        virReportError(errno,
                       _("cannot create directory %s"),
                       driver->stateDir);
        goto error;
    }

    if ((driver->lockFD =
         virPidFileAcquire(driver->stateDir, "driver",
                           false, getpid())) < 0)
        goto error;

    if (virStoragePoolObjLoadAllState(driver->pools,
                                      driver->stateDir) < 0)
        goto error;

    if (virStoragePoolObjLoadAllConfigs(driver->pools,
                                        driver->configDir,
                                        driver->autostartDir) < 0)
        goto error;

    storagePoolUpdateAllState();

    if (virDriverShouldAutostart(driver->stateDir, &autostart) < 0)
        goto error;

    if (autostart)
        storageDriverAutostart();

    driver->storageEventState = virObjectEventStateNew();

    /* Only one load of storage driver plus backends exists. Unlike
     * domains where new binaries could change the capabilities. A
     * new/changed backend requires a reinitialization. */
    if (!(driver->caps = virStorageBackendGetCapabilities()))
        goto error;

    storageDriverUnlock();

    return VIR_DRV_STATE_INIT_COMPLETE;

 error:
    storageDriverUnlock();
    storageStateCleanup();
    return VIR_DRV_STATE_INIT_ERROR;
}


storageVolGetInfoFlags(virStorageVolPtr vol,
                       virStorageVolInfoPtr info,
                       unsigned int flags)
{
    virStoragePoolObj *obj;
    virStorageBackend *backend;
    virStorageVolDef *voldef;
    int ret = -1;

    virCheckFlags(VIR_STORAGE_VOL_GET_PHYSICAL, -1);

    if (!(voldef = virStorageVolDefFromVol(vol, &obj, &backend)))
        return -1;

    if (virStorageVolGetInfoFlagsEnsureACL(vol->conn,
                                           virStoragePoolObjGetDef(obj),
                                           voldef) < 0)
        goto cleanup;

    if (backend->refreshVol &&
        backend->refreshVol(obj, voldef) < 0)
        goto cleanup;

    memset(info, 0, sizeof(*info));
    info->type = voldef->type;
    info->capacity = voldef->target.capacity;
    if (flags & VIR_STORAGE_VOL_GET_PHYSICAL)
        info->allocation = voldef->target.physical;
    else
        info->allocation = voldef->target.allocation;
    ret = 0;

 cleanup:
    virStoragePoolObjEndAPI(&obj);
    return ret;
}


storagePoolRefreshFailCleanup(virStorageBackend *backend,
                              virStoragePoolObj *obj,
                              const char *stateFile)
{
    virErrorPtr orig_err;

    virErrorPreserveLast(&orig_err);
    virStoragePoolObjClearVols(obj);

    if (stateFile)
        unlink(stateFile);
    if (backend->stopPool)
        backend->stopPool(obj);
    virErrorRestore(&orig_err);
}


storageConnectListStoragePools(virConnectPtr conn,
                               char **const names,
                               int maxnames)
{
    if (virConnectListStoragePoolsEnsureACL(conn) < 0)
        return -1;

    return virStoragePoolObjGetNames(driver->pools, conn, true,
                                     virConnectListStoragePoolsCheckACL,
                                     names, maxnames);
}


storageRegisterFull(bool allbackends)
{
    if (virRegisterConnectDriver(&storageConnectDriver, false) < 0)
        return -1;
    if (virStorageBackendDriversRegister(allbackends) < 0)
        return -1;
    if (virSetSharedStorageDriver(&storageDriver) < 0)
        return -1;
    if (virRegisterStateDriver(&stateDriver) < 0)
        return -1;
    return 0;
}


storagePoolLookupByVolume(virStorageVolPtr vol)
{
    virStoragePoolObj *obj;
    virStoragePoolDef *def;
    virStoragePoolPtr pool = NULL;

    if (!(obj = storagePoolObjFindByName(vol->pool)))
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByVolumeEnsureACL(vol->conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(vol->conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjEndAPI(&obj);
    return pool;
}


storageDriverAutostartCallback(virStoragePoolObj *obj,
                               const void *opaque G_GNUC_UNUSED)
{
    virStoragePoolDef *def = virStoragePoolObjGetDef(obj);
    virStorageBackend *backend;
    bool started = false;

    if (!(backend = virStorageBackendForType(def->type)))
        return;

    if (virStoragePoolObjIsAutostart(obj) &&
        !virStoragePoolObjIsActive(obj)) {

        virStoragePoolObjSetStarting(obj, true);
        if (backend->startPool &&
            backend->startPool(obj) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Failed to autostart storage pool '%s': %s"),
                           def->name, virGetLastErrorMessage());
            goto cleanup;
        }
        started = true;
    }

    if (started) {
        g_autofree char *stateFile = NULL;

        stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");
        if (!stateFile ||
            virStoragePoolSaveState(stateFile, def) < 0 ||
            storagePoolRefreshImpl(backend, obj, stateFile) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Failed to autostart storage pool '%s': %s"),
                           def->name, virGetLastErrorMessage());
        } else {
            virStoragePoolObjSetActive(obj, true);
        }
    }

 cleanup:
    if (virStoragePoolObjIsStarting(obj)) {
        if (!virStoragePoolObjIsActive(obj))
            virStoragePoolUpdateInactive(obj);
        virStoragePoolObjSetStarting(obj, false);
    }
}


storagePoolIsPersistent(virStoragePoolPtr pool)
{
    virStoragePoolObj *obj;
    virStoragePoolDef *def;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolIsPersistentEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    ret = virStoragePoolObjGetConfigFile(obj) ? 1 : 0;

 cleanup:
    virStoragePoolObjEndAPI(&obj);
    return ret;
}


static int data_pending(const struct Curl_easy *data)
{
  struct connectdata *conn = data->conn;

#ifdef ENABLE_QUIC
  if(conn->transport == TRNSPRT_QUIC)
    return Curl_quic_data_pending(data);
#endif

  if(conn->handler->protocol&PROTO_FAMILY_FTP)
    return Curl_ssl_data_pending(conn, SECONDARYSOCKET);

  /* in the case of libssh2, we can never be really sure that we have emptied
     its internal buffers so we MUST always try until we get EAGAIN back */
  return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
#ifdef USE_NGHTTP2
    /* For HTTP/2, we may read up everything including response body
       with header fields in Curl_http_readwrite_headers. If no
       content-length is provided, curl waits for the connection
       close, which we emulate it using conn->proto.httpc.closed =
       TRUE. The thing is if we read everything, then http2_recv won't
       be called and we cannot signal the HTTP/2 stream has closed. As
       a workaround, we return nonzero here to call http2_recv. */
    ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) ||
#endif
    Curl_ssl_data_pending(conn, FIRSTSOCKET);
}


CURLcode Curl_readrewind(struct Curl_easy *data)
{
  struct connectdata *conn = data->conn;
  curl_mimepart *mimepart = &data->set.mimepost;

  conn->bits.rewindaftersend = FALSE; /* we rewind now */

  /* explicitly switch off sending data on this connection now since we are
     about to restart a new transfer and thus we want to avoid inadvertently
     sending more data on the existing connection until the next transfer
     starts */
  data->req.keepon &= ~KEEP_SEND;

  /* We have sent away data. If not using CURLOPT_POSTFIELDS or
     CURLOPT_HTTPPOST, call app to rewind
  */
  if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
    struct HTTP *http = data->req.p.http;

    if(http->sendit)
      mimepart = http->sendit;
  }
  if(data->set.postfields)
    ; /* do nothing */
  else if(data->state.httpreq == HTTPREQ_POST_MIME ||
          data->state.httpreq == HTTPREQ_POST_FORM) {
    CURLcode result = Curl_mime_rewind(mimepart);
    if(result) {
      failf(data, "Cannot rewind mime/post data");
      return result;
    }
  }
  else {
    if(data->set.seek_func) {
      int err;

      Curl_set_in_callback(data, true);
      err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
      Curl_set_in_callback(data, false);
      if(err) {
        failf(data, "seek callback returned error %d", (int)err);
        return CURLE_SEND_FAIL_REWIND;
      }
    }
    else if(data->set.ioctl_func) {
      curlioerr err;

      Curl_set_in_callback(data, true);
      err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
                                   data->set.ioctl_client);
      Curl_set_in_callback(data, false);
      infof(data, "the ioctl callback returned %d", (int)err);

      if(err) {
        failf(data, "ioctl callback returned error %d", (int)err);
        return CURLE_SEND_FAIL_REWIND;
      }
    }
    else {
      /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
         given FILE * stream and we can actually attempt to rewind that
         ourselves with fseek() */
      if(data->state.fread_func == (curl_read_callback)fread) {
        if(-1 != fseek(data->state.in, 0, SEEK_SET))
          /* successful rewind */
          return CURLE_OK;
      }

      /* no callback set or failure above, makes us fail at once */
      failf(data, "necessary data rewind wasn't possible");
      return CURLE_SEND_FAIL_REWIND;
    }
  }
  return CURLE_OK;
}


static void put_avc_eos_tag(AVIOContext *pb, unsigned ts)
{
    avio_w8(pb, FLV_TAG_TYPE_VIDEO);
    avio_wb24(pb, 5);               /* Tag Data Size */
    avio_wb24(pb, ts);              /* lower 24 bits of timestamp in ms */
    avio_w8(pb, (ts >> 24) & 0x7F); /* MSB of ts in ms */
    avio_wb24(pb, 0);               /* StreamId = 0 */
    avio_w8(pb, 23);                /* ub[4] FrameType = 1, ub[4] CodecId = 7 */
    avio_w8(pb, 2);                 /* AVC end of sequence */
    avio_wb24(pb, 0);               /* Always 0 for AVC EOS. */
    avio_wb32(pb, 16);              /* Size of FLV tag */
}


obj_stream_position (Bit_Chain *restrict dat, Bit_Chain *restrict hdl_dat,
                     Bit_Chain *restrict str_dat)
{
  unsigned long p1 = bit_position (dat);
  /* all 3 now relative to obj */
  unsigned long p2 = bit_position (hdl_dat);
  SINCE (R_2007)
    { // but only since 2007 there is a seperate string stream
      unsigned long p3 = bit_position (str_dat);
      if (p2 > p1)
        return p3 > p2 ? p3 : p2;
      else
        return p3 > p1 ? p3 : p1;
    }
  else
    {
      return (p2 > p1) ? p2 : p1;
    }
}


section_string_stream (Dwg_Data *restrict dwg, Bit_Chain *restrict dat, BITCODE_RL bitsize,
                       Bit_Chain *restrict str)
{
  BITCODE_RL start;     // in bits
  BITCODE_RL data_size; // in bits
  BITCODE_B endbit;
  if (dwg->header.version < R_2010 ||
      (dwg->header.version == R_2010 && dwg->header.maint_version < 3))
  {
    // r2007: + 24 bytes (sentinel+size+hsize) - 1 bit (endbit)
    start = bitsize + 159;
  }
  else
  {
    // r2010: + 24 bytes (sentinel+size+hSize) - 1 bit (endbit)
    start = bitsize + 191; /* 8*24 = 192 */
  }
  *str = *dat;
  bit_set_position (str, start);
  LOG_TRACE ("section string stream\n  pos: " FORMAT_RL ", %lu/%u\n", start,
             str->byte, str->bit);
  endbit = bit_read_B (str);
  LOG_HANDLE ("  endbit: %d\n", (int)endbit);
  if (!endbit)
    return; // i.e. has no strings. without data_size should be 0
  start -= 16;
  bit_set_position (str, start);
  LOG_HANDLE ("  pos: " FORMAT_RL ", %lu\n", start, str->byte);
  // str->bit = start & 7;
  data_size = bit_read_RS (str);
  LOG_HANDLE ("  data_size: " FORMAT_RL "\n", data_size);
  if (data_size & 0x8000)
    {
      BITCODE_RS hi_size;
      start -= 16;
      data_size &= 0x7FFF;
      bit_set_position (str, start);
      LOG_HANDLE ("  pos: " FORMAT_RL ", %lu\n", start, str->byte);
      hi_size = bit_read_RS (str);
      data_size |= (hi_size << 15);
      LOG_HANDLE ("  hi_size: " FORMAT_RS ", data_size: " FORMAT_RL "\n",
                  hi_size, data_size);
    }
  start -= data_size;
  bit_set_position (str, start);
  LOG_HANDLE ("  pos: " FORMAT_RL ", %lu/%u\n", start, str->byte, str->bit);
}


read_sections_map (Bit_Chain *dat, int64_t size_comp, int64_t size_uncomp,
                   int64_t correction)
{
  BITCODE_RC *data;
  r2007_section *sections = NULL, *last_section = NULL, *section = NULL;
  BITCODE_RC *ptr, *ptr_end;
  int i, j = 0;

  data = read_system_page (dat, size_comp, size_uncomp, correction);
  if (!data)
    {
      LOG_ERROR ("Failed to read system page")
      return NULL;
    }

  ptr = data;
  ptr_end = data + size_uncomp;

  LOG_TRACE ("\n=== System Section (Section Map) ===\n")

  while (ptr < ptr_end)
    {
      section = (r2007_section *)calloc (1, sizeof (r2007_section));
      if (!section)
        {
          LOG_ERROR ("Out of memory");
          free (data);
          sections_destroy (sections); // the root
          return NULL;
        }

      bfr_read (section, &ptr, 64);

      LOG_TRACE ("\nSection [%d]:\n", j)
      LOG_TRACE ("  data size:     %" PRIu64 "\n", section->data_size)
      LOG_TRACE ("  max size:      %" PRIu64 "\n", section->max_size)
      LOG_TRACE ("  encryption:    %" PRIu64 "\n", section->encrypted)
      LOG_HANDLE ("  hashcode:      %" PRIx64 "\n", section->hashcode)
      LOG_HANDLE ("  name length:   %" PRIu64 "\n", section->name_length)
      LOG_TRACE ("  unknown:       %" PRIu64 "\n", section->unknown)
      LOG_TRACE ("  encoding:      %" PRIu64 "\n", section->encoded)
      LOG_TRACE ("  num pages:     %" PRIu64 "\n", section->num_pages);

      // debugging sanity
#if 1
      /* compressed */
      if (section->data_size > 10 * dat->size
          || section->name_length >= (int64_t)dat->size
          || section->name_length >= 48)
        {
          LOG_ERROR ("Invalid System Section");
          free (section);
          free (data);
          sections_destroy (sections); // the root
          return NULL;
        }
        // assert(section->data_size < dat->size + 0x100000);
        // assert(section->max_size  < dat->size + 0x100000);
        // assert(section->num_pages < DBG_MAX_COUNT);
#endif
      // section->next = NULL;
      // section->pages = NULL;
      // section->name = NULL;

      if (!sections)
        {
          sections = last_section = section;
        }
      else
        {
          last_section->next = section;
          last_section = section;
        }

      j++;
      if (ptr >= ptr_end)
        break;

      // Section Name (wchar)
      section->name = bfr_read_string (&ptr, section->name_length);
#ifdef HAVE_NATIVE_WCHAR2
      LOG_TRACE ("  name:          " FORMAT_TU "\n\n",
                 (BITCODE_TU)section->name)
#else
      LOG_TRACE ("  name:          ")
      LOG_TEXT_UNICODE (TRACE, section->name)
      LOG_TRACE ("\n\n")
#endif
      section->type = dwg_section_wtype (section->name);

      if (section->num_pages <= 0)
        continue;
      if (section->num_pages > 0xf0000)
        {
          LOG_ERROR ("Invalid num_pages %lu, skip", (unsigned long)section->num_pages);
          continue;
        }

      section->pages = (r2007_section_page **)calloc (
          (size_t)section->num_pages, sizeof (r2007_section_page *));
      if (!section->pages)
        {
          LOG_ERROR ("Out of memory");
          free (data);
          if (sections)
            sections_destroy (sections); // the root
          else
            sections_destroy (section);
          return NULL;
        }

      for (i = 0; i < section->num_pages; i++)
        {
          section->pages[i]
              = (r2007_section_page *)calloc (1, sizeof (r2007_section_page));
          if (!section->pages[i])
            {
              LOG_ERROR ("Out of memory");
              free (data);
              if (sections)
                sections_destroy (sections); // the root
              else
                sections_destroy (section);
              return NULL;
            }

          if (ptr + 56 >= ptr_end)
            {
              LOG_ERROR ("Section[%d]->pages[%d] overflow", j, i);
              free (section->pages[i]);
              section->num_pages = i; // skip this last section
              break;
            }
          bfr_read (section->pages[i], &ptr, 56);

          LOG_TRACE (" Page[%d]: ", i)
          LOG_TRACE (" offset: 0x%07" PRIx64, section->pages[i]->offset);
          LOG_TRACE (" size: %5" PRIu64, section->pages[i]->size);
          LOG_TRACE (" id: %4" PRIu64, section->pages[i]->id);
          LOG_TRACE (" uncomp_size: %5" PRIu64 "\n",
                     section->pages[i]->uncomp_size);
          LOG_HANDLE (" comp_size: %5" PRIu64, section->pages[i]->comp_size);
          LOG_HANDLE (" checksum: %016" PRIx64, section->pages[i]->checksum);
          LOG_HANDLE (" crc64: %016" PRIx64 "\n", section->pages[i]->crc);
          // debugging sanity
          if (section->pages[i]->size >= DBG_MAX_SIZE
              || section->pages[i]->uncomp_size >= DBG_MAX_SIZE
              || section->pages[i]->comp_size >= DBG_MAX_SIZE)
            {
              LOG_ERROR ("Invalid section->pages[%d] size", i);
              free (data);
              free (section->pages[i]);
              section->num_pages = i; // skip this last section
              return sections;
            }
          assert (section->pages[i]->size < DBG_MAX_SIZE);
          assert (section->pages[i]->uncomp_size < DBG_MAX_SIZE);
          assert (section->pages[i]->comp_size < DBG_MAX_SIZE);
        }
    }

  free (data);
  return sections;
}


void fs_private(void) {
	char *homedir = cfg.homedir;
	assert(homedir);
	uid_t u = getuid();
	gid_t g = getgid();

	int xflag = store_xauthority();
	int aflag = store_asoundrc();

	// mask /home
	if (arg_debug)
		printf("Mounting a new /home directory\n");
	if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC,  "mode=755,gid=0") < 0)
		errExit("mounting home directory");
	fs_logger("tmpfs /home");

	// mask /root
	if (arg_debug)
		printf("Mounting a new /root directory\n");
	if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_STRICTATIME | MS_REC,  "mode=700,gid=0") < 0)
		errExit("mounting root directory");
	fs_logger("tmpfs /root");

	if (u != 0) {
		// create /home/user
		if (arg_debug)
			printf("Create a new user directory\n");
		if (mkdir(homedir, S_IRWXU) == -1) {
			if (mkpath_as_root(homedir) == -1)
				errExit("mkpath");
			if (mkdir(homedir, S_IRWXU) == -1)
				errExit("mkdir");
		}
		if (chown(homedir, u, g) < 0)
			errExit("chown");
		fs_logger2("mkdir", homedir);
	}

	skel(homedir, u, g);
	if (xflag)
		copy_xauthority();
	if (aflag)
		copy_asoundrc();

}


const char *get_panic_action(void)
{
	const char *cmd_fmt = getenv("RSYNC_PANIC_ACTION");

	if (cmd_fmt)
		return cmd_fmt;
	return "xterm -display :0 -T Panic -n Panic -e gdb /proc/%d/exe %d";
}


static void sigusr1_handler(UNUSED(int val))
{
	called_from_signal_handler = 1;
	exit_cleanup(RERR_SIGNAL1);
}


static void forward_filesfrom_data(void)
{
	ssize_t len;

	len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
	if (len <= 0) {
		if (len == 0 || errno != EINTR) {
			/* Send end-of-file marker */
			ff_forward_fd = -1;
			write_buf(iobuf.out_fd, "\0\0", ff_lastchar ? 2 : 1);
			free_xbuf(&ff_xb);
			if (ff_reenable_multiplex >= 0)
				io_start_multiplex_out(ff_reenable_multiplex);
		}
		return;
	}

	if (DEBUG_GTE(IO, 2)) {
		rprintf(FINFO, "[%s] files-from read=%" SIZE_T_FMT_MOD "d\n",
			who_am_i(), (SIZE_T_FMT_CAST)len);
	}

#ifdef ICONV_OPTION
	len += ff_xb.len;
#endif

	if (!eol_nulls) {
		char *s = ff_xb.buf + len;
		/* Transform CR and/or LF into '\0' */
		while (s-- > ff_xb.buf) {
			if (*s == '\n' || *s == '\r')
				*s = '\0';
		}
	}

	if (ff_lastchar)
		ff_xb.pos = 0;
	else {
		char *s = ff_xb.buf;
		/* Last buf ended with a '\0', so don't let this buf start with one. */
		while (len && *s == '\0')
			s++, len--;
		ff_xb.pos = s - ff_xb.buf;
	}

#ifdef ICONV_OPTION
	if (filesfrom_convert && len) {
		char *sob = ff_xb.buf + ff_xb.pos, *s = sob;
		char *eob = sob + len;
		int flags = ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT;
		if (ff_lastchar == '\0')
			flags |= ICB_INIT;
		/* Convert/send each null-terminated string separately, skipping empties. */
		while (s != eob) {
			if (*s++ == '\0') {
				ff_xb.len = s - sob - 1;
				add_implied_include(sob);
				if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
					exit_cleanup(RERR_PROTOCOL); /* impossible? */
				write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
				while (s != eob && *s == '\0')
					s++;
				sob = s;
				ff_xb.pos = sob - ff_xb.buf;
				flags |= ICB_INIT;
			}
		}

		if ((ff_xb.len = s - sob) == 0)
			ff_lastchar = '\0';
		else {
			/* Handle a partial string specially, saving any incomplete chars. */
			flags &= ~ICB_INCLUDE_INCOMPLETE;
			if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
				if (errno == E2BIG)
					exit_cleanup(RERR_PROTOCOL); /* impossible? */
				if (ff_xb.pos)
					memmove(ff_xb.buf, ff_xb.buf + ff_xb.pos, ff_xb.len);
			}
			ff_lastchar = 'x'; /* Anything non-zero. */
		}
	} else
#endif

	if (len) {
		char *f = ff_xb.buf + ff_xb.pos;
		char *t = ff_xb.buf;
		char *eob = f + len;
		char *cur = t;
		/* Eliminate any multi-'\0' runs. */
		while (f != eob) {
			if (!(*t++ = *f++)) {
				add_implied_include(cur);
				cur = t;
				while (f != eob && *f == '\0')
					f++;
			}
		}
		ff_lastchar = f[-1];
		if ((len = t - ff_xb.buf) != 0) {
			/* This will not circle back to perform_io() because we only get
			 * called when there is plenty of room in the output buffer. */
			write_buf(iobuf.out_fd, ff_xb.buf, len);
		}
	}
}


static void teardown_mergelist(filter_rule *ex)
{
	int j;

	if (!ex->u.mergelist)
		return;

	if (DEBUG_GTE(FILTER, 2)) {
		rprintf(FINFO, "[%s] deactivating mergelist #%d%s\n",
			who_am_i(), mergelist_cnt - 1,
			ex->u.mergelist->debug_type);
	}

	free(ex->u.mergelist->debug_type);
	free(ex->u.mergelist);

	for (j = 0; j < mergelist_cnt; j++) {
		if (mergelist_parents[j] == ex) {
			mergelist_parents[j] = NULL;
			break;
		}
	}
	while (mergelist_cnt && mergelist_parents[mergelist_cnt-1] == NULL)
		mergelist_cnt--;
}


static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
			   int flags)
{
	struct dirent *di;
	unsigned remainder;
	char *p;
	DIR *d;
	int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0;
	int start = flist->used;
	int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;

	assert(flist != NULL);

	if (!(d = opendir(fbuf))) {
		if (errno == ENOENT) {
			if (am_sender) /* Can abuse this for vanished error w/ENOENT: */
				interpret_stat_error(fbuf, True);
			return;
		}
		if (errno == ENOTDIR && (flags & FLAG_PERHAPS_DIR))
			return;
		io_error |= IOERR_GENERAL;
		rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
		return;
	}

	p = fbuf + len;
	if (len == 1 && *fbuf == '/')
		remainder = MAXPATHLEN - 1;
	else if (len < MAXPATHLEN-1) {
		*p++ = '/';
		*p = '\0';
		remainder = MAXPATHLEN - (len + 1);
	} else
		remainder = 0;

	for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
		unsigned name_len;
		char *dname = d_name(di);
		if (dname[0] == '.' && (dname[1] == '\0'
		    || (dname[1] == '.' && dname[2] == '\0')))
			continue;
		name_len = strlcpy(p, dname, remainder);
		if (name_len >= remainder) {
			char save = fbuf[len];
			fbuf[len] = '\0';
			io_error |= IOERR_GENERAL;
			rprintf(FERROR_XFER,
				"filename overflows max-path len by %u: %s/%s\n",
				name_len - remainder + 1, fbuf, dname);
			fbuf[len] = save;
			continue;
		}
		if (dname[0] == '\0') {
			io_error |= IOERR_GENERAL;
			rprintf(FERROR_XFER,
				"cannot send file with empty name in %s\n",
				full_fname(fbuf));
			continue;
		}

		send_file_name(f, flist, fbuf, NULL, flags, filter_level);
	}

	fbuf[len] = '\0';

	if (errno) {
		io_error |= IOERR_GENERAL;
		rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf));
	}

	closedir(d);

	if (f >= 0 && recurse && !divert_dirs) {
		int i, end = flist->used - 1;
		/* send_if_directory() bumps flist->used, so use "end". */
		for (i = start; i <= end; i++)
			send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
	}
}


int shell_exec(const char *cmd)
{
	char *shell = getenv("RSYNC_SHELL");
	int status;
	pid_t pid;

	if (!shell)
		return system(cmd);

	if ((pid = fork()) < 0)
		return -1;

	if (pid == 0) {
		execlp(shell, shell, "-c", cmd, NULL);
		_exit(1);
	}

	int ret = wait_process(pid, &status, 0);
	return ret < 0 ? -1 : status;
}


static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
{
	static int64 modtime, atime;
#ifdef SUPPORT_CRTIMES
	static time_t crtime;
#endif
	static mode_t mode;
#ifdef SUPPORT_HARD_LINKS
	static int64 dev;
#endif
	static dev_t rdev;
	static uint32 rdev_major;
	static uid_t uid;
	static gid_t gid;
	static uint16 gid_flags;
	static char lastname[MAXPATHLEN], *lastdir;
	static int lastdir_depth, lastdir_len = -1;
	static unsigned int del_hier_name_len = 0;
	static int in_del_hier = 0;
	char thisname[MAXPATHLEN];
	unsigned int l1 = 0, l2 = 0;
	int alloc_len, basename_len, linkname_len;
	int extra_len = file_extra_cnt * EXTRA_LEN;
	int first_hlink_ndx = -1;
	char real_ISREG_entry;
	int64 file_length;
#ifdef CAN_SET_NSEC
	uint32 modtime_nsec;
#endif
	const char *basename;
	struct file_struct *file;
	alloc_pool_t *pool;
	char *bp;

	if (xflags & XMIT_SAME_NAME)
		l1 = read_byte(f);

	if (xflags & XMIT_LONG_NAME)
		l2 = read_varint30(f);
	else
		l2 = read_byte(f);

	if (l2 >= MAXPATHLEN - l1) {
		rprintf(FERROR,
			"overflow: xflags=0x%x l1=%d l2=%d lastname=%s [%s]\n",
			xflags, l1, l2, lastname, who_am_i());
		overflow_exit("recv_file_entry");
	}

	strlcpy(thisname, lastname, l1 + 1);
	read_sbuf(f, &thisname[l1], l2);
	thisname[l1 + l2] = 0;

	/* Abuse basename_len for a moment... */
	basename_len = strlcpy(lastname, thisname, MAXPATHLEN);

#ifdef ICONV_OPTION
	if (ic_recv != (iconv_t)-1) {
		xbuf outbuf, inbuf;

		INIT_CONST_XBUF(outbuf, thisname);
		INIT_XBUF(inbuf, lastname, basename_len, (size_t)-1);

		if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
			io_error |= IOERR_GENERAL;
			rprintf(FERROR_UTF8,
			    "[%s] cannot convert filename: %s (%s)\n",
			    who_am_i(), lastname, strerror(errno));
			outbuf.len = 0;
		}
		thisname[outbuf.len] = '\0';
	}
#endif

	if (*thisname
	 && (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
		rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
		exit_cleanup(RERR_PROTOCOL);
	}

	if (sanitize_paths)
		sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);

	if ((basename = strrchr(thisname, '/')) != NULL) {
		int len = basename++ - thisname;
		if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
			lastdir = new_array(char, len + 1);
			memcpy(lastdir, thisname, len);
			lastdir[len] = '\0';
			lastdir_len = len;
			lastdir_depth = count_dir_elements(lastdir);
		}
	} else
		basename = thisname;
	basename_len = strlen(basename) + 1; /* count the '\0' */

#ifdef SUPPORT_HARD_LINKS
	if (protocol_version >= 30
	 && BITS_SETnUNSET(xflags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
		first_hlink_ndx = read_varint(f);
		if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->ndx_start + flist->used) {
			rprintf(FERROR,
				"hard-link reference out of range: %d (%d)\n",
				first_hlink_ndx, flist->ndx_start + flist->used);
			exit_cleanup(RERR_PROTOCOL);
		}
		if (DEBUG_GTE(HLINK, 1)) {
			rprintf(FINFO, "[%s] #%d hard-links #%d (%sabbrev)\n",
				who_am_i(), flist->used+flist->ndx_start, first_hlink_ndx,
				first_hlink_ndx >= flist->ndx_start ? "" : "un");
		}
		if (first_hlink_ndx >= flist->ndx_start) {
			struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
			file_length = F_LENGTH(first);
			modtime = first->modtime;
#ifdef CAN_SET_NSEC
			modtime_nsec = F_MOD_NSEC_or_0(first);
#endif
			mode = first->mode;
			if (atimes_ndx && !S_ISDIR(mode))
				atime = F_ATIME(first);
#ifdef SUPPORT_CRTIMES
			if (crtimes_ndx)
				crtime = F_CRTIME(first);
#endif
			if (preserve_uid)
				uid = F_OWNER(first);
			if (preserve_gid)
				gid = F_GROUP(first);
			if (preserve_devices && IS_DEVICE(mode)) {
				uint32 *devp = F_RDEV_P(first);
				rdev_major = DEV_MAJOR(devp);
				rdev = MAKEDEV(rdev_major, DEV_MINOR(devp));
				extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
			}
			if (preserve_links && S_ISLNK(mode))
				linkname_len = strlen(F_SYMLINK(first)) + 1;
			else
				linkname_len = 0;
			real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
			goto create_object;
		}
	}
#endif

	file_length = read_varlong30(f, 3);
	if (!(xflags & XMIT_SAME_TIME)) {
		if (protocol_version >= 30) {
			modtime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
			if (!am_generator && (int64)(time_t)modtime != modtime) {
				rprintf(FERROR_XFER,
				    "Time value of %s truncated on receiver.\n",
				    lastname);
			}
#endif
		} else
			modtime = read_int(f);
	}
	if (xflags & XMIT_MOD_NSEC)
#ifndef CAN_SET_NSEC
		(void)read_varint(f);
#else
		modtime_nsec = read_varint(f);
	else
		modtime_nsec = 0;
#endif
#ifdef SUPPORT_CRTIMES
	if (crtimes_ndx) {
		if (xflags & XMIT_CRTIME_EQ_MTIME)
			crtime = modtime;
		else
			crtime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
		if (!am_generator && (int64)(time_t)crtime != crtime) {
			rprintf(FERROR_XFER,
				"Create time value of %s truncated on receiver.\n",
				lastname);
		}
#endif
	}
#endif
	if (!(xflags & XMIT_SAME_MODE))
		mode = from_wire_mode(read_int(f));
	if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
		atime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
		if (!am_generator && (int64)(time_t)atime != atime) {
			rprintf(FERROR_XFER,
				"Access time value of %s truncated on receiver.\n",
				lastname);
		}
#endif
	}

	if (chmod_modes && !S_ISLNK(mode) && mode)
		mode = tweak_mode(mode, chmod_modes);

	if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
		if (protocol_version < 30)
			uid = (uid_t)read_int(f);
		else {
			uid = (uid_t)read_varint(f);
			if (xflags & XMIT_USER_NAME_FOLLOWS)
				uid = recv_user_name(f, uid);
			else if (inc_recurse && am_root && (!numeric_ids || usermap))
				uid = match_uid(uid);
		}
	}
	if (preserve_gid && !(xflags & XMIT_SAME_GID)) {
		if (protocol_version < 30)
			gid = (gid_t)read_int(f);
		else {
			gid = (gid_t)read_varint(f);
			gid_flags = 0;
			if (xflags & XMIT_GROUP_NAME_FOLLOWS)
				gid = recv_group_name(f, gid, &gid_flags);
			else if (inc_recurse && (!am_root || !numeric_ids || groupmap))
				gid = match_gid(gid, &gid_flags);
		}
	}

	if ((preserve_devices && IS_DEVICE(mode))
	 || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
		if (protocol_version < 28) {
			if (!(xflags & XMIT_SAME_RDEV_pre28))
				rdev = (dev_t)read_int(f);
		} else {
			uint32 rdev_minor;
			if (!(xflags & XMIT_SAME_RDEV_MAJOR))
				rdev_major = read_varint30(f);
			if (protocol_version >= 30)
				rdev_minor = read_varint(f);
			else if (xflags & XMIT_RDEV_MINOR_8_pre30)
				rdev_minor = read_byte(f);
			else
				rdev_minor = read_int(f);
			rdev = MAKEDEV(rdev_major, rdev_minor);
		}
		if (IS_DEVICE(mode))
			extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
		file_length = 0;
	} else if (protocol_version < 28)
		rdev = MAKEDEV(0, 0);

#ifdef SUPPORT_LINKS
	if (preserve_links && S_ISLNK(mode)) {
		linkname_len = read_varint30(f) + 1; /* count the '\0' */
		if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
			rprintf(FERROR, "overflow: linkname_len=%d\n",
				linkname_len - 1);
			overflow_exit("recv_file_entry");
		}
#ifdef ICONV_OPTION
		/* We don't know how much extra room we need to convert
		 * the as-yet-unread symlink data, so let's hope that a
		 * double-size buffer is plenty. */
		if (sender_symlink_iconv)
			linkname_len *= 2;
#endif
		if (munge_symlinks)
			linkname_len += SYMLINK_PREFIX_LEN;
	}
	else
#endif
		linkname_len = 0;

	if (copy_devices && IS_DEVICE(mode)) {
		/* This is impossible in the official release, but some pre-release patches
		 * didn't convert the device into a file before sending, so we'll do it here
		 * (even though the length is typically 0 and any checksum data is zeros). */
		mode = S_IFREG | (mode & ACCESSPERMS);
		modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
		real_ISREG_entry = 0;
	} else
		real_ISREG_entry = S_ISREG(mode) ? 1 : 0;

#ifdef SUPPORT_HARD_LINKS
  create_object:
	if (preserve_hard_links) {
		if (protocol_version < 28 && real_ISREG_entry)
			xflags |= XMIT_HLINKED;
		if (xflags & XMIT_HLINKED)
			extra_len += (inc_recurse+1) * EXTRA_LEN;
	}
#endif

#ifdef SUPPORT_ACLS
	/* Directories need an extra int32 for the default ACL. */
	if (preserve_acls && S_ISDIR(mode))
		extra_len += EXTRA_LEN;
#endif

	if (always_checksum && S_ISREG(mode))
		extra_len += SUM_EXTRA_CNT * EXTRA_LEN;

#if SIZEOF_INT64 >= 8
	if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
		extra_len += EXTRA_LEN;
#endif
#ifdef CAN_SET_NSEC
	if (modtime_nsec)
		extra_len += EXTRA_LEN;
#endif
	if (file_length < 0) {
		rprintf(FERROR, "Offset underflow: file-length is negative\n");
		exit_cleanup(RERR_UNSUPPORTED);
	}

	if (*thisname != '.' || thisname[1] != '\0') {
		int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
		if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
		 && filter_list.head && check_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
			rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
			exit_cleanup(RERR_PROTOCOL);
		}
		if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
			rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
			exit_cleanup(RERR_PROTOCOL);
		}
	}

	if (inc_recurse && S_ISDIR(mode)) {
		if (one_file_system) {
			/* Room to save the dir's device for -x */
			extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
		}
		pool = dir_flist->file_pool;
	} else
		pool = flist->file_pool;

#if EXTRA_ROUNDING > 0
	if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
		extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
#endif

	alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
		  + linkname_len;
	bp = pool_alloc(pool, alloc_len, "recv_file_entry");

	memset(bp, 0, extra_len + FILE_STRUCT_LEN);
	bp += extra_len;
	file = (struct file_struct *)bp;
	bp += FILE_STRUCT_LEN;

	memcpy(bp, basename, basename_len);

#ifdef SUPPORT_HARD_LINKS
	if (xflags & XMIT_HLINKED
#ifndef CAN_HARDLINK_SYMLINK
	 && !S_ISLNK(mode)
#endif
#ifndef CAN_HARDLINK_SPECIAL
	 && !IS_SPECIAL(mode) && !IS_DEVICE(mode)
#endif
	)
		file->flags |= FLAG_HLINKED;
#endif
	file->modtime = (time_t)modtime;
#ifdef CAN_SET_NSEC
	if (modtime_nsec) {
		file->flags |= FLAG_MOD_NSEC;
		F_MOD_NSEC(file) = modtime_nsec;
	}
#endif
	file->len32 = (uint32)file_length;
#if SIZEOF_INT64 >= 8
	if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
#if SIZEOF_CAPITAL_OFF_T < 8
		rprintf(FERROR, "Offset overflow: attempted 64-bit file-length\n");
		exit_cleanup(RERR_UNSUPPORTED);
#else
		file->flags |= FLAG_LENGTH64;
		F_HIGH_LEN(file) = (uint32)(file_length >> 32);
#endif
	}
#endif
	file->mode = mode;
	if (preserve_uid)
		F_OWNER(file) = uid;
	if (preserve_gid) {
		F_GROUP(file) = gid;
		file->flags |= gid_flags;
	}
	if (atimes_ndx && !S_ISDIR(mode))
		F_ATIME(file) = atime;
#ifdef SUPPORT_CRTIMES
	if (crtimes_ndx)
		F_CRTIME(file) = crtime;
#endif
	if (unsort_ndx)
		F_NDX(file) = flist->used + flist->ndx_start;

	if (basename != thisname) {
		file->dirname = lastdir;
		F_DEPTH(file) = lastdir_depth + 1;
	} else
		F_DEPTH(file) = 1;

	if (S_ISDIR(mode)) {
		if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */
			F_DEPTH(file)--;
		if (protocol_version >= 30) {
			if (!(xflags & XMIT_NO_CONTENT_DIR)) {
				if (xflags & XMIT_TOP_DIR)
					file->flags |= FLAG_TOP_DIR;
				file->flags |= FLAG_CONTENT_DIR;
			} else if (xflags & XMIT_TOP_DIR)
				file->flags |= FLAG_IMPLIED_DIR;
		} else if (xflags & XMIT_TOP_DIR) {
			in_del_hier = recurse;
			del_hier_name_len = F_DEPTH(file) == 0 ? 0 : l1 + l2;
			if (relative_paths && del_hier_name_len > 2
			    && lastname[del_hier_name_len-1] == '.'
			    && lastname[del_hier_name_len-2] == '/')
				del_hier_name_len -= 2;
			file->flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
		} else if (in_del_hier) {
			if (!relative_paths || !del_hier_name_len
			 || (l1 >= del_hier_name_len
			  && lastname[del_hier_name_len] == '/'))
				file->flags |= FLAG_CONTENT_DIR;
			else
				in_del_hier = 0;
		}
	}

	if (preserve_devices && IS_DEVICE(mode)) {
		uint32 *devp = F_RDEV_P(file);
		DEV_MAJOR(devp) = major(rdev);
		DEV_MINOR(devp) = minor(rdev);
	}

#ifdef SUPPORT_LINKS
	if (linkname_len) {
		bp += basename_len;
		if (first_hlink_ndx >= flist->ndx_start) {
			struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
			memcpy(bp, F_SYMLINK(first), linkname_len);
		} else {
			if (munge_symlinks) {
				strlcpy(bp, SYMLINK_PREFIX, linkname_len);
				bp += SYMLINK_PREFIX_LEN;
				linkname_len -= SYMLINK_PREFIX_LEN;
			}
#ifdef ICONV_OPTION
			if (sender_symlink_iconv) {
				xbuf outbuf, inbuf;

				alloc_len = linkname_len;
				linkname_len /= 2;

				/* Read the symlink data into the end of our double-sized
				 * buffer and then convert it into the right spot. */
				INIT_XBUF(inbuf, bp + alloc_len - linkname_len,
					  linkname_len - 1, (size_t)-1);
				read_sbuf(f, inbuf.buf, inbuf.len);
				INIT_XBUF(outbuf, bp, 0, alloc_len);

				if (iconvbufs(ic_recv, &inbuf, &outbuf, ICB_INIT) < 0) {
					io_error |= IOERR_GENERAL;
					rprintf(FERROR_XFER,
					    "[%s] cannot convert symlink data for: %s (%s)\n",
					    who_am_i(), full_fname(thisname), strerror(errno));
					bp = (char*)file->basename;
					*bp++ = '\0';
					outbuf.len = 0;
				}
				bp[outbuf.len] = '\0';
			} else
#endif
				read_sbuf(f, bp, linkname_len - 1);
			if (sanitize_paths && !munge_symlinks && *bp)
				sanitize_path(bp, bp, "", lastdir_depth, SP_DEFAULT);
		}
	}
#endif

#ifdef SUPPORT_HARD_LINKS
	if (preserve_hard_links && xflags & XMIT_HLINKED) {
		if (protocol_version >= 30) {
			if (xflags & XMIT_HLINK_FIRST) {
				F_HL_GNUM(file) = flist->ndx_start + flist->used;
			} else
				F_HL_GNUM(file) = first_hlink_ndx;
		} else {
			static int32 cnt = 0;
			struct ht_int64_node *np;
			int64 ino;
			int32 ndx;
			if (protocol_version < 26) {
				dev = read_int(f);
				ino = read_int(f);
			} else {
				if (!(xflags & XMIT_SAME_DEV_pre30))
					dev = read_longint(f);
				ino = read_longint(f);
			}
			np = idev_find(dev, ino);
			ndx = (int32)(long)np->data; /* is -1 when new */
			if (ndx < 0) {
				np->data = (void*)(long)cnt;
				ndx = cnt++;
			}
			F_HL_GNUM(file) = ndx;
		}
	}
#endif

	if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
		if (real_ISREG_entry)
			bp = F_SUM(file);
		else {
			/* Prior to 28, we get a useless set of nulls. */
			bp = tmp_sum;
		}
		if (first_hlink_ndx >= flist->ndx_start) {
			struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
			memcpy(bp, F_SUM(first), flist_csum_len);
		} else
			read_buf(f, bp, flist_csum_len);
	}

#ifdef SUPPORT_ACLS
	if (preserve_acls && !S_ISLNK(mode))
		receive_acl(f, file);
#endif
#ifdef SUPPORT_XATTRS
	if (preserve_xattrs)
		receive_xattr(f, file);
#endif

	if (S_ISREG(mode) || S_ISLNK(mode))
		stats.total_size += file_length;

	return file;
}


uint32_t ThreadCommand::count() const {
  return count_;
}


static inline RBinWasmCustomNameLocalNames *parse_custom_names_local(RBuffer *b, ut64 bound) {
	RBinWasmCustomNameLocalNames *local = R_NEW0 (RBinWasmCustomNameLocalNames);
	if (!local) {
		return NULL;
	}
	if (!consume_u32_r (b, bound, &local->count)) {
		goto beach;
	}

	local->locals = r_list_newf ((RListFree)wasm_custom_name_local_free);
	if (local->locals) {
		size_t i;
		for (i = 0; i < local->count; i++) {
			RBinWasmCustomNameLocalName *local_name = parse_local_name (b, bound);
			if (!local_name || !r_list_append (local->locals, local_name)) {
				wasm_custom_name_local_free (local_name);
				goto beach;
			}
		}
		return local;
	}

beach:
	wasm_custom_local_names_free (local);
	return NULL;
}


RPVector *r_bin_wasm_get_types(RBinWasmObj *bin) {
	r_return_val_if_fail (bin && bin->g_sections, NULL);
	return bin->g_types? bin->g_types: parse_unique_subsec_vec_by_id (bin, R_BIN_WASM_SECTION_TYPE);
}


static RBinWasmDataEntry *parse_data_entry(RBinWasmObj *bin, ut64 bound, ut32 index) {
	RBuffer *b = bin->buf;
	RBinWasmDataEntry *ptr = R_NEW0 (RBinWasmDataEntry);
	if (!ptr) {
		return NULL;
	}
	ptr->sec_i = index;
	ptr->file_offset = r_buf_tell (b);
	if (!consume_u32_r (b, bound, &ptr->index)) {
		goto beach;
	}
	if (!(ptr->offset.len = consume_init_expr_r (b, bound, R_BIN_WASM_END_OF_CODE, NULL))) {
		goto beach;
	}
	if (!consume_u32_r (b, bound, &ptr->size)) {
		goto beach;
	}
	ptr->data = r_buf_tell (b);
	r_buf_seek (b, ptr->size, R_BUF_CUR);
	return ptr;

beach:
	free (ptr);
	return NULL;
}


static RBinWasmMemoryEntry *parse_memory_entry(RBinWasmObj *bin, ut64 bound, ut32 index) {
	RBuffer *b = bin->buf;
	RBinWasmMemoryEntry *ptr = R_NEW0 (RBinWasmMemoryEntry);
	if (ptr) {
		ptr->sec_i = index;
		ptr->file_offset = r_buf_tell (b);
		if (!consume_limits_r (b, bound, &ptr->limits)) {
			free (ptr);
			return NULL;
		}
	}
	return ptr;
}


RPVector *r_bin_wasm_get_imports(RBinWasmObj *bin) {
	r_return_val_if_fail (bin && bin->g_sections, NULL);
	return bin->g_imports? bin->g_imports: parse_unique_subsec_vec_by_id (bin, R_BIN_WASM_SECTION_IMPORT);
}


RPVector *r_bin_wasm_get_codes(RBinWasmObj *bin) {
	r_return_val_if_fail (bin && bin->g_sections, NULL);
	return bin->g_codes? bin->g_codes: parse_unique_subsec_vec_by_id (bin, R_BIN_WASM_SECTION_CODE);
}


static bool consume_str_r(RBuffer *b, ut64 bound, size_t len, char *out) {
	r_return_val_if_fail (b && bound > 0 && bound < r_buf_size (b), 0);
	*out = 0;

	if (r_buf_tell (b) + len <= bound + 1) {
		if (r_buf_read (b, (ut8 *)out, len) == len) {
			out[len] = 0;
			return true;
		}
	}
	return false;
}


CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize)
{
	STPFileHeader fileHeader;
	if(!file.ReadStruct(fileHeader))
	{
		return ProbeWantMoreData;
	}
	if(!ValidateHeader(fileHeader))
	{
		return ProbeFailure;
	}
	MPT_UNREFERENCED_PARAMETER(pfilesize);
	return ProbeSuccess;
}


static inline __m128i SSE2_mm_abs_epi16(__m128i a) {
	__m128i t = _mm_srai_epi16(a, 15);
	return _mm_xor_si128(_mm_add_epi16(a, t), t);
}


static void quantsmooth_block(JCOEFPTR coef, UINT16 *quantval,
		JSAMPLE *image, JSAMPLE *image2, int stride, int flags, float **tables, int luma) {
	int k, n = DCTSIZE, x, y, need_refresh = 1;
	JSAMPLE ALIGN(32) buf[DCTSIZE2 + DCTSIZE * 6], *border = buf + n * n;
#ifndef NO_SIMD
	int16_t ALIGN(32) temp[DCTSIZE2 * 4 + DCTSIZE * (4 - 2)];
#endif
#ifdef USE_JSIMD
	JSAMPROW output_buf[DCTSIZE]; int output_col = 0;
	for (k = 0; k < n; k++) output_buf[k] = buf + k * n;
#endif
	(void)x;

	if (image2) {
		float ALIGN(32) fbuf[DCTSIZE2];
#if 1 && defined(USE_NEON)
		for (y = 0; y < n; y++) {
			uint8x8_t h0, h1; uint16x8_t sumA, sumB, v0, v1;
			uint16x4_t h2, h3; float32x4_t v5, scale;
			uint32x4_t v4, sumAA1, sumAB1, sumAA2, sumAB2;
#define M1(xx, yy) \
	h0 = vld1_u8(&image2[(y + yy) * stride + xx]); \
	h1 = vld1_u8(&image[(y + yy) * stride + xx]); \
	sumA = vaddw_u8(sumA, h0); v0 = vmull_u8(h0, h0); \
	sumB = vaddw_u8(sumB, h1); v1 = vmull_u8(h0, h1); \
	sumAA1 = vaddw_u16(sumAA1, vget_low_u16(v0)); \
	sumAB1 = vaddw_u16(sumAB1, vget_low_u16(v1)); \
	sumAA2 = vaddw_u16(sumAA2, vget_high_u16(v0)); \
	sumAB2 = vaddw_u16(sumAB2, vget_high_u16(v1));
#define M2 \
	sumA = vaddq_u16(sumA, sumA); sumB = vaddq_u16(sumB, sumB); \
	sumAA1 = vaddq_u32(sumAA1, sumAA1); sumAA2 = vaddq_u32(sumAA2, sumAA2); \
	sumAB1 = vaddq_u32(sumAB1, sumAB1); sumAB2 = vaddq_u32(sumAB2, sumAB2);
			h0 = vld1_u8(&image2[y * stride]);
			h1 = vld1_u8(&image[y * stride]);
			sumA = vmovl_u8(h0); v0 = vmull_u8(h0, h0);
			sumB = vmovl_u8(h1); v1 = vmull_u8(h0, h1);
			sumAA1 = vmovl_u16(vget_low_u16(v0));
			sumAB1 = vmovl_u16(vget_low_u16(v1));
			sumAA2 = vmovl_u16(vget_high_u16(v0));
			sumAB2 = vmovl_u16(vget_high_u16(v1));
			M2 M1(0, -1) M1(-1, 0) M1(1, 0) M1(0, 1)
			M2 M1(-1, -1) M1(1, -1) M1(-1, 1) M1(1, 1)
#undef M2
#undef M1
			v0 = vmovl_u8(vld1_u8(&image2[y * stride]));
#define M1(low, sumAA, sumAB, x) \
	h2 = vget_##low##_u16(sumA); sumAA = vshlq_n_u32(sumAA, 4); \
	h3 = vget_##low##_u16(sumB); sumAB = vshlq_n_u32(sumAB, 4); \
	sumAA = vmlsl_u16(sumAA, h2, h2); sumAB = vmlsl_u16(sumAB, h2, h3); \
	v4 = vtstq_u32(sumAA, sumAA); \
	sumAB = vandq_u32(sumAB, v4); sumAA = vornq_u32(sumAA, v4); \
	scale = vdivq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(sumAB)), \
			vcvtq_f32_s32(vreinterpretq_s32_u32(sumAA))); \
	scale = vmaxq_f32(scale, vdupq_n_f32(-16.0f)); \
	scale = vminq_f32(scale, vdupq_n_f32(16.0f)); \
	v4 = vshll_n_u16(vget_##low##_u16(v0), 4); \
	v5 = vcvtq_n_f32_s32(vreinterpretq_s32_u32(vsubw_u16(v4, h2)), 4); \
	v5 = vmlaq_f32(vcvtq_n_f32_u32(vmovl_u16(h3), 4), v5, scale); \
	v5 = vmaxq_f32(v5, vdupq_n_f32(0)); \
	v5 = vsubq_f32(v5, vdupq_n_f32(CENTERJSAMPLE)); \
	v5 = vminq_f32(v5, vdupq_n_f32(CENTERJSAMPLE)); \
	vst1q_f32(fbuf + y * n + x, v5);
			M1(low, sumAA1, sumAB1, 0) M1(high, sumAA2, sumAB2, 4)
#undef M1
		}
#elif 1 && defined(USE_AVX2)
		for (y = 0; y < n; y++) {
			__m128i v0, v1; __m256i v2, v3, v4, sumA, sumB, sumAA, sumAB;
			__m256 v5, scale;
#define M1(x0, y0, x1, y1) \
	v0 = _mm_loadl_epi64((__m128i*)&image2[(y + y0) * stride + x0]); \
	v1 = _mm_loadl_epi64((__m128i*)&image2[(y + y1) * stride + x1]); \
	v2 = _mm256_cvtepu8_epi16(_mm_unpacklo_epi8(v0, v1)); \
	v0 = _mm_loadl_epi64((__m128i*)&image[(y + y0) * stride + x0]); \
	v1 = _mm_loadl_epi64((__m128i*)&image[(y + y1) * stride + x1]); \
	v3 = _mm256_cvtepu8_epi16(_mm_unpacklo_epi8(v0, v1)); \
	sumA = _mm256_add_epi16(sumA, v2); \
	sumB = _mm256_add_epi16(sumB, v3); \
	sumAA = _mm256_add_epi32(sumAA, _mm256_madd_epi16(v2, v2)); \
	sumAB = _mm256_add_epi32(sumAB, _mm256_madd_epi16(v2, v3));
			v0 = _mm_loadl_epi64((__m128i*)&image2[y * stride]);
			v1 = _mm_loadl_epi64((__m128i*)&image[y * stride]);
			sumA = _mm256_cvtepu8_epi16(_mm_unpacklo_epi8(v0, v0));
			sumB = _mm256_cvtepu8_epi16(_mm_unpacklo_epi8(v1, v1));
			sumAA = _mm256_madd_epi16(sumA, sumA);
			sumAB = _mm256_madd_epi16(sumA, sumB);
			M1(0, -1, -1, 0) M1(1, 0, 0, 1)
			sumA = _mm256_add_epi16(sumA, sumA); sumAA = _mm256_add_epi32(sumAA, sumAA);
			sumB = _mm256_add_epi16(sumB, sumB); sumAB = _mm256_add_epi32(sumAB, sumAB);
			M1(-1, -1, 1, -1) M1(-1, 1, 1, 1)
#undef M1
			v3 = _mm256_set1_epi16(1);
			v2 = _mm256_madd_epi16(sumA, v3); sumAA = _mm256_slli_epi32(sumAA, 4);
			v3 = _mm256_madd_epi16(sumB, v3); sumAB = _mm256_slli_epi32(sumAB, 4);
			sumAA = _mm256_sub_epi32(sumAA, _mm256_mullo_epi32(v2, v2));
			sumAB = _mm256_sub_epi32(sumAB, _mm256_mullo_epi32(v2, v3));
			v4 = _mm256_cmpeq_epi32(sumAA, _mm256_setzero_si256());
			sumAB = _mm256_andnot_si256(v4, sumAB);
			scale = _mm256_cvtepi32_ps(_mm256_or_si256(sumAA, v4));
			scale = _mm256_div_ps(_mm256_cvtepi32_ps(sumAB), scale);
			scale = _mm256_max_ps(scale, _mm256_set1_ps(-16.0f));
			scale = _mm256_min_ps(scale, _mm256_set1_ps(16.0f));
			v0 = _mm_loadl_epi64((__m128i*)&image2[y * stride]);
			v4 = _mm256_slli_epi32(_mm256_cvtepu8_epi32(v0), 4);
			v5 = _mm256_cvtepi32_ps(_mm256_sub_epi32(v4, v2));
			// v5 = _mm256_add_ps(_mm256_mul_ps(v5, scale), _mm256_cvtepi32_ps(v3));
			v5 = _mm256_fmadd_ps(v5, scale, _mm256_cvtepi32_ps(v3));
			v5 = _mm256_mul_ps(v5, _mm256_set1_ps(1.0f / 16));
			v5 = _mm256_max_ps(v5, _mm256_setzero_ps());
			v5 = _mm256_sub_ps(v5, _mm256_set1_ps(CENTERJSAMPLE));
			v5 = _mm256_min_ps(v5, _mm256_set1_ps(CENTERJSAMPLE));
			_mm256_storeu_ps(fbuf + y * n, v5);
		}
#elif 1 && defined(USE_SSE2)
		for (y = 0; y < n; y++) {
			__m128i v0, v1, v2, v3, v4, sumA, sumB, sumAA1, sumAB1, sumAA2, sumAB2;
			__m128 v5, scale;
#define M1(x0, y0, x1, y1) \
	v0 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image2[(y + y0) * stride + x0])); \
	v1 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image2[(y + y1) * stride + x1])); \
	v2 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[(y + y0) * stride + x0])); \
	v3 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[(y + y1) * stride + x1])); \
	sumA = _mm_add_epi16(_mm_add_epi16(sumA, v0), v1); \
	sumB = _mm_add_epi16(_mm_add_epi16(sumB, v2), v3); \
	v4 = _mm_unpacklo_epi16(v0, v1); sumAA1 = _mm_add_epi32(sumAA1, _mm_madd_epi16(v4, v4)); \
	v1 = _mm_unpackhi_epi16(v0, v1); sumAA2 = _mm_add_epi32(sumAA2, _mm_madd_epi16(v1, v1)); \
	sumAB1 = _mm_add_epi32(sumAB1, _mm_madd_epi16(v4, _mm_unpacklo_epi16(v2, v3))); \
	sumAB2 = _mm_add_epi32(sumAB2, _mm_madd_epi16(v1, _mm_unpackhi_epi16(v2, v3)));
			v0 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image2[y * stride]));
			v1 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[y * stride]));
			v2 = _mm_unpacklo_epi16(v0, v0); sumAA1 = _mm_madd_epi16(v2, v2);
			v3 = _mm_unpacklo_epi16(v1, v1); sumAB1 = _mm_madd_epi16(v2, v3);
			v2 = _mm_unpackhi_epi16(v0, v0); sumAA2 = _mm_madd_epi16(v2, v2);
			v3 = _mm_unpackhi_epi16(v1, v1); sumAB2 = _mm_madd_epi16(v2, v3);
			sumA = _mm_add_epi16(v0, v0); sumB = _mm_add_epi16(v1, v1);
			M1(0, -1, -1, 0) M1(1, 0, 0, 1)
			sumA = _mm_add_epi16(sumA, sumA); sumB = _mm_add_epi16(sumB, sumB);
			sumAA1 = _mm_add_epi32(sumAA1, sumAA1); sumAA2 = _mm_add_epi32(sumAA2, sumAA2);
			sumAB1 = _mm_add_epi32(sumAB1, sumAB1); sumAB2 = _mm_add_epi32(sumAB2, sumAB2);
			M1(-1, -1, 1, -1) M1(-1, 1, 1, 1)
#undef M1
			v0 = _mm_setzero_si128();
			v1 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image2[y * stride]));
#define M1(lo, sumAA, sumAB, x) \
	v2 = _mm_unpack##lo##_epi16(sumA, v0); sumAA = _mm_slli_epi32(sumAA, 4); \
	v3 = _mm_unpack##lo##_epi16(sumB, v0); sumAB = _mm_slli_epi32(sumAB, 4); \
	sumAA = _mm_sub_epi32(sumAA, _mm_mullo_epi32(v2, v2)); \
	sumAB = _mm_sub_epi32(sumAB, _mm_mullo_epi32(v2, v3)); \
	v4 = _mm_cmpeq_epi32(sumAA, v0); sumAB = _mm_andnot_si128(v4, sumAB); \
	scale = _mm_cvtepi32_ps(_mm_or_si128(sumAA, v4)); \
	scale = _mm_div_ps(_mm_cvtepi32_ps(sumAB), scale); \
	scale = _mm_max_ps(scale, _mm_set1_ps(-16.0f)); \
	scale = _mm_min_ps(scale, _mm_set1_ps(16.0f)); \
	v4 = _mm_slli_epi32(_mm_unpack##lo##_epi16(v1, v0), 4); \
	v5 = _mm_cvtepi32_ps(_mm_sub_epi32(v4, v2)); \
	v5 = _mm_add_ps(_mm_mul_ps(v5, scale), _mm_cvtepi32_ps(v3)); \
	v5 = _mm_mul_ps(v5, _mm_set1_ps(1.0f / 16)); \
	v5 = _mm_max_ps(v5, _mm_setzero_ps()); \
	v5 = _mm_sub_ps(v5, _mm_set1_ps(CENTERJSAMPLE)); \
	v5 = _mm_min_ps(v5, _mm_set1_ps(CENTERJSAMPLE)); \
	_mm_storeu_ps(fbuf + y * n + x, v5);
			M1(lo, sumAA1, sumAB1, 0) M1(hi, sumAA2, sumAB2, 4)
#undef M1
		}
#else
		for (y = 0; y < n; y++)
		for (x = 0; x < n; x++) {
			float sumA = 0, sumB = 0, sumAA = 0, sumAB = 0;
			float divN = 1.0f / 16, scale, offset; float a;
#define M1(xx, yy) { \
	float a = image2[(y + yy) * stride + x + xx]; \
	float b = image[(y + yy) * stride + x + xx]; \
	sumA += a; sumAA += a * a; \
	sumB += b; sumAB += a * b; }
#define M2 sumA += sumA; sumB += sumB; \
	sumAA += sumAA; sumAB += sumAB;
			M1(0, 0) M2
			M1(0, -1) M1(-1, 0) M1(1, 0) M1(0, 1) M2
			M1(-1, -1) M1(1, -1) M1(-1, 1) M1(1, 1)
#undef M2
#undef M1
			scale = sumAA - sumA * divN * sumA;
			if (scale != 0.0f) scale = (sumAB - sumA * divN * sumB) / scale;
			scale = scale < -16.0f ? -16.0f : scale;
			scale = scale > 16.0f ? 16.0f : scale;
			offset = (sumB - scale * sumA) * divN;

			a = image2[y * stride + x] * scale + offset;
			a = a < 0 ? 0 : a > MAXJSAMPLE + 1 ? MAXJSAMPLE + 1 : a;
			fbuf[y * n + x] = a - CENTERJSAMPLE;
		}
#endif
		fdct_clamp(fbuf, coef, quantval);
	}

	if (flags & JPEGQS_LOW_QUALITY) {
		float ALIGN(32) fbuf[DCTSIZE2];
		float range = 0, c0 = 2, c1 = c0 * sqrtf(0.5f);

		if (image2) goto end;
		{
			int sum = 0;
			for (x = 1; x < n * n; x++) {
				int a = coef[x]; a = a < 0 ? -a : a;
				range += quantval[x] * a; sum += a;
			}
			if (sum) range *= 4.0f / sum;
			if (range > CENTERJSAMPLE) range = CENTERJSAMPLE;
			range = roundf(range);
		}

#if 1 && defined(USE_NEON)
		for (y = 0; y < n; y++) {
			int16x8_t v4, v5; uint16x8_t v6 = vdupq_n_u16((int)range);
			float32x2_t f4; uint8x8_t i0, i1;
			float32x4_t f0, f1, s0 = vdupq_n_f32(0), s1 = s0, s2 = s0, s3 = s0;
			f4 = vset_lane_f32(c1, vdup_n_f32(c0), 1);
			i0 = vld1_u8(&image[y * stride]);
#define M1(i, x, y) \
	i1 = vld1_u8(&image[(y) * stride + x]); \
	v4 = vreinterpretq_s16_u16(vsubl_u8(i0, i1)); \
	v5 = vreinterpretq_s16_u16(vqsubq_u16(v6, \
			vreinterpretq_u16_s16(vabsq_s16(v4)))); \
	M2(low, s0, s1, i) M2(high, s2, s3, i)
#define M2(low, s0, s1, i) \
	f0 = vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v5))); \
	f0 = vmulq_f32(f0, f0); f1 = vmulq_lane_f32(f0, f4, i); \
	f0 = vmulq_f32(f0, vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v4)))); \
	s0 = vmlaq_f32(s0, f0, f1); s1 = vmlaq_f32(s1, f1, f1);
			M1(1, -1, y-1) M1(0, 0, y-1) M1(1, 1, y-1)
			M1(0, -1, y)                 M1(0, 1, y)
			M1(1, -1, y+1) M1(0, 0, y+1) M1(1, 1, y+1)
#undef M1
#undef M2
			v4 = vreinterpretq_s16_u16(vmovl_u8(i0));
#define M1(low, s0, s1, x) \
	f1 = vbslq_f32(vceqq_f32(s1, vdupq_n_f32(0)), vdupq_n_f32(1.0f), s1); \
	f0 = vdivq_f32(s0, f1); \
	f1 = vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v4))); \
	f0 = vsubq_f32(f1, f0); \
	f0 = vsubq_f32(f0, vdupq_n_f32(CENTERJSAMPLE)); \
	vst1q_f32(fbuf + y * n + x, f0);
			M1(low, s0, s1, 0) M1(high, s2, s3, 4)
#undef M1
		}
#elif 1 && defined(USE_AVX512)
		for (y = 0; y < n; y += 2) {
			__m256i v0, v1, v4, v5, v6 = _mm256_set1_epi16((int)range);
			__m512 f0, f1, f4, f5, s0 = _mm512_setzero_ps(), s1 = s0; __mmask16 m0;
			f4 = _mm512_set1_ps(c0); f5 = _mm512_set1_ps(c1);
#define M2(v0, pos) \
	v0 = _mm256_cvtepu8_epi16(_mm_unpacklo_epi64( \
			_mm_loadl_epi64((__m128i*)&image[pos]), \
			_mm_loadl_epi64((__m128i*)&image[pos + stride])));
#define M1(f4, x, y) M2(v1, (y) * stride + x) \
	v4 = _mm256_sub_epi16(v0, v1); v5 = _mm256_subs_epu16(v6, _mm256_abs_epi16(v4)); \
	f0 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(v5)); \
	f0 = _mm512_mul_ps(f0, f0); f1 = _mm512_mul_ps(f0, f4); \
	f0 = _mm512_mul_ps(f0, _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(v4))); \
	s0 = _mm512_fmadd_ps(f0, f1, s0); s1 = _mm512_fmadd_ps(f1, f1, s1);
			M2(v0, y * stride)
			M1(f5, -1, y-1) M1(f4, 0, y-1) M1(f5, 1, y-1)
			M1(f4, -1, y)                  M1(f4, 1, y)
			M1(f5, -1, y+1) M1(f4, 0, y+1) M1(f5, 1, y+1)
#undef M1
#undef M2
			m0 = _mm512_cmp_ps_mask(s1, _mm512_setzero_ps(), 0);
			s1 = _mm512_mask_blend_ps(m0, s1, _mm512_set1_ps(1.0f));
			f0 = _mm512_div_ps(s0, s1);
			f1 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(v0));
			f0 = _mm512_sub_ps(f1, f0);
			f0 = _mm512_sub_ps(f0, _mm512_set1_ps(CENTERJSAMPLE));
			_mm512_storeu_ps(fbuf + y * n, f0);
		}
#elif 1 && defined(USE_AVX2)
		for (y = 0; y < n; y++) {
			__m128i v0, v1, v4, v5, v6 = _mm_set1_epi16((int)range);
			__m256 f0, f1, f4, f5, s0 = _mm256_setzero_ps(), s1 = s0;
			f4 = _mm256_set1_ps(c0); f5 = _mm256_set1_ps(c1);
			v0 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[y * stride]));
#define M1(f4, x, y) \
	v1 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[(y) * stride + x])); \
	v4 = _mm_sub_epi16(v0, v1); v5 = _mm_subs_epu16(v6, _mm_abs_epi16(v4)); \
	f0 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v5)); \
	f0 = _mm256_mul_ps(f0, f0); f1 = _mm256_mul_ps(f0, f4); \
	f0 = _mm256_mul_ps(f0, _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v4))); \
	s0 = _mm256_fmadd_ps(f0, f1, s0); s1 = _mm256_fmadd_ps(f1, f1, s1);
			M1(f5, -1, y-1) M1(f4, 0, y-1) M1(f5, 1, y-1)
			M1(f4, -1, y)                  M1(f4, 1, y)
			M1(f5, -1, y+1) M1(f4, 0, y+1) M1(f5, 1, y+1)
#undef M1
			f1 = _mm256_cmp_ps(s1, _mm256_setzero_ps(), 0);
			s1 = _mm256_blendv_ps(s1, _mm256_set1_ps(1.0f), f1);
			f0 = _mm256_div_ps(s0, s1);
			f1 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v0));
			f0 = _mm256_sub_ps(f1, f0);
			f0 = _mm256_sub_ps(f0, _mm256_set1_ps(CENTERJSAMPLE));
			_mm256_storeu_ps(fbuf + y * n, f0);
		}
#elif 1 && defined(USE_SSE2)
		for (y = 0; y < n; y++) {
			__m128i v0, v1, v3, v4, v5, v6 = _mm_set1_epi16((int)range), v7 = _mm_setzero_si128();
			__m128 f0, f1, f4, f5, s0 = _mm_setzero_ps(), s1 = s0, s2 = s0, s3 = s0;
			f4 = _mm_set1_ps(c0); f5 = _mm_set1_ps(c1);
			v0 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[y * stride]));
#define M1(f4, x, y) \
	v1 = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)&image[(y) * stride + x])); \
	v4 = _mm_sub_epi16(v0, v1); v3 = _mm_srai_epi16(v4, 15); \
	v5 = _mm_subs_epu16(v6, _mm_abs_epi16(v4)); \
	M2(lo, s0, s1, f4) M2(hi, s2, s3, f4)
#define M2(lo, s0, s1, f4) \
	f0 = _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v5, v7)); \
	f0 = _mm_mul_ps(f0, f0); f1 = _mm_mul_ps(f0, f4); \
	f0 = _mm_mul_ps(f0, _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v4, v3))); \
	f0 = _mm_mul_ps(f0, f1); f1 = _mm_mul_ps(f1, f1); \
	s0 = _mm_add_ps(s0, f0); s1 = _mm_add_ps(s1, f1);
			M1(f5, -1, y-1) M1(f4, 0, y-1) M1(f5, 1, y-1)
			M1(f4, -1, y)                  M1(f4, 1, y)
			M1(f5, -1, y+1) M1(f4, 0, y+1) M1(f5, 1, y+1)
#undef M1
#undef M2
#define M1(lo, s0, s1, x) \
	f1 = _mm_cmpeq_ps(s1, _mm_setzero_ps()); \
	f1 = _mm_and_ps(f1, _mm_set1_ps(1.0f)); \
	f0 = _mm_div_ps(s0, _mm_or_ps(s1, f1)); \
	f1 = _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v0, v7)); \
	f0 = _mm_sub_ps(f1, f0); \
	f0 = _mm_sub_ps(f0, _mm_set1_ps(CENTERJSAMPLE)); \
	_mm_storeu_ps(fbuf + y * n + x, f0);
			M1(lo, s0, s1, 0) M1(hi, s2, s3, 4)
#undef M1
		}
#else
		for (y = 0; y < n; y++)
		for (x = 0; x < n; x++) {
#define M1(i, x, y) t0 = a - image[(y) * stride + x]; \
	t = range - fabsf(t0); t = t < 0 ? 0 : t; t *= t; aw = c##i * t; \
	a0 += t0 * t * aw; an += aw * aw;
			int a = image[(y)*stride+(x)];
			float a0 = 0, an = 0, aw, t, t0;
			M1(1, x-1, y-1) M1(0, x, y-1) M1(1, x+1, y-1)
			M1(0, x-1, y)                 M1(0, x+1, y)
			M1(1, x-1, y+1) M1(0, x, y+1) M1(1, x+1, y+1)
#undef M1
			if (an > 0.0f) a -= a0 / an;
			fbuf[y * n + x] = a - CENTERJSAMPLE;
		}
#endif
		fdct_clamp(fbuf, coef, quantval);
		goto end;
	}

#if 1 && defined(USE_NEON)
#define VINITD uint8x8_t i0, i1, i2;
#define VDIFF(i) vst1q_u16((uint16_t*)temp + (i) * n, vsubl_u8(i0, i1));
#define VLDPIX(j, p) i##j = vld1_u8(p);
#define VRIGHT(a, b) i##a = vext_u8(i##b, i##b, 1);
#define VCOPY(a, b) i##a = i##b;

#define VINIT \
	int16x8_t v0, v5; uint16x8_t v6 = vdupq_n_u16(range); \
	float32x4_t f0, f1, s0 = vdupq_n_f32(0), s1 = s0, s2 = s0, s3 = s0;

#define VCORE \
	v0 = vld1q_s16(temp + y * n); \
	v5 = vreinterpretq_s16_u16(vqsubq_u16(v6, \
			vreinterpretq_u16_s16(vabsq_s16(v0)))); \
	VCORE1(low, s0, s1, tab) VCORE1(high, s2, s3, tab + 4)

#define VCORE1(low, s0, s1, tab) \
	f0 = vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v5))); \
	f0 = vmulq_f32(f0, f0); f1 = vmulq_f32(f0, vld1q_f32(tab + y * n)); \
	f0 = vmulq_f32(f0, vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v0)))); \
	s0 = vmlaq_f32(s0, f0, f1); s1 = vmlaq_f32(s1, f1, f1);

#ifdef __aarch64__
#define VFIN \
	a2 = vaddvq_f32(vaddq_f32(s0, s2)); \
	a3 = vaddvq_f32(vaddq_f32(s1, s3));
#else
#define VFIN { \
	float32x4x2_t p0; float32x2_t v0; \
	p0 = vzipq_f32(vaddq_f32(s0, s2), vaddq_f32(s1, s3)); \
	f0 = vaddq_f32(p0.val[0], p0.val[1]); \
	v0 = vadd_f32(vget_low_f32(f0), vget_high_f32(f0)); \
	a2 = vget_lane_f32(v0, 0); a3 = vget_lane_f32(v0, 1); \
}
#endif

#elif 1 && defined(USE_AVX512)
#define VINCR 2
#define VINIT \
	__m256i v4, v5, v6 = _mm256_set1_epi16(range); \
	__m512 f0, f1, f4, s0 = _mm512_setzero_ps(), s1 = s0;

#define VCORE \
	v4 = _mm256_loadu_si256((__m256i*)&temp[y * n]); \
	f4 = _mm512_load_ps(tab + y * n); \
	v5 = _mm256_subs_epu16(v6, _mm256_abs_epi16(v4)); \
	f0 = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(v5)); \
	f0 = _mm512_mul_ps(f0, f0); f1 = _mm512_mul_ps(f0, f4); \
	f0 = _mm512_mul_ps(f0, _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(v4))); \
	s0 = _mm512_fmadd_ps(f0, f1, s0); s1 = _mm512_fmadd_ps(f1, f1, s1);

// "reduce_add" is not faster here, because it's a macro, not a single instruction
// a2 = _mm512_reduce_add_ps(s0); a3 = _mm512_reduce_add_ps(s1);
#define VFIN { __m256 s2, s3, f2; \
	f0 = _mm512_shuffle_f32x4(s0, s1, 0x44); \
	f1 = _mm512_shuffle_f32x4(s0, s1, 0xee); \
	f0 = _mm512_add_ps(f0, f1); s2 = _mm512_castps512_ps256(f0); \
	s3 = _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(f0), 1)); \
	f2 = _mm256_permute2f128_ps(s2, s3, 0x20); \
	f2 = _mm256_add_ps(f2, _mm256_permute2f128_ps(s2, s3, 0x31)); \
	f2 = _mm256_add_ps(f2, _mm256_shuffle_ps(f2, f2, 0xee)); \
	f2 = _mm256_add_ps(f2, _mm256_shuffle_ps(f2, f2, 0x55)); \
	a2 = _mm256_cvtss_f32(f2); \
	a3 = _mm_cvtss_f32(_mm256_extractf128_ps(f2, 1)); }

#elif 1 && defined(USE_AVX2)
#define VINIT \
	__m128i v4, v5, v6 = _mm_set1_epi16(range); \
	__m256 f0, f1, f4, s0 = _mm256_setzero_ps(), s1 = s0;

#define VCORE \
	v4 = _mm_loadu_si128((__m128i*)&temp[y * n]); \
	f4 = _mm256_load_ps(tab + y * n); \
	v5 = _mm_subs_epu16(v6, _mm_abs_epi16(v4)); \
	f0 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v5)); \
	f0 = _mm256_mul_ps(f0, f0); f1 = _mm256_mul_ps(f0, f4); \
	f0 = _mm256_mul_ps(f0, _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v4))); \
	s0 = _mm256_fmadd_ps(f0, f1, s0); s1 = _mm256_fmadd_ps(f1, f1, s1);

#define VFIN \
	f0 = _mm256_permute2f128_ps(s0, s1, 0x20); \
	f1 = _mm256_permute2f128_ps(s0, s1, 0x31); \
	f0 = _mm256_add_ps(f0, f1); \
	f0 = _mm256_add_ps(f0, _mm256_shuffle_ps(f0, f0, 0xee)); \
	f0 = _mm256_add_ps(f0, _mm256_shuffle_ps(f0, f0, 0x55)); \
	a2 = _mm256_cvtss_f32(f0); \
	a3 = _mm_cvtss_f32(_mm256_extractf128_ps(f0, 1));

#elif 1 && defined(USE_SSE2)
#define VINIT \
	__m128i v3, v4, v5, v6 = _mm_set1_epi16(range), v7 = _mm_setzero_si128(); \
	__m128 f0, f1, s0 = _mm_setzero_ps(), s1 = s0, s2 = s0, s3 = s0;

#define VCORE \
	v4 = _mm_loadu_si128((__m128i*)&temp[y * n]); \
	v3 = _mm_srai_epi16(v4, 15); \
	v5 = _mm_subs_epu16(v6, _mm_abs_epi16(v4)); \
	VCORE1(lo, s0, s1, tab) VCORE1(hi, s2, s3, tab + 4)

#define VCORE1(lo, s0, s1, tab) \
	f0 = _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v5, v7)); \
	f0 = _mm_mul_ps(f0, f0); \
	f1 = _mm_mul_ps(f0, _mm_load_ps(tab + y * n)); \
	f0 = _mm_mul_ps(f0, _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v4, v3))); \
	f0 = _mm_mul_ps(f0, f1); f1 = _mm_mul_ps(f1, f1); \
	s0 = _mm_add_ps(s0, f0); s1 = _mm_add_ps(s1, f1);

#define VFIN \
	f0 = _mm_add_ps(s0, s2); f1 = _mm_add_ps(s1, s3); \
	f0 = _mm_add_ps(_mm_unpacklo_ps(f0, f1), _mm_unpackhi_ps(f0, f1)); \
	f0 = _mm_add_ps(f0, _mm_shuffle_ps(f0, f0, 0xee)); \
	a2 = _mm_cvtss_f32(f0); \
	a3 = _mm_cvtss_f32(_mm_shuffle_ps(f0, f0, 0x55));

#elif !defined(NO_SIMD) // vector code simulation
#define VINITD JSAMPLE *p0, *p1, *p2;
#define VDIFF(i) for (x = 0; x < n; x++) temp[(i) * n + x] = p0[x] - p1[x];
#define VLDPIX(i, a) p##i = a;
#define VRIGHT(a, b) p##a = p##b + 1;
#define VCOPY(a, b) p##a = p##b;

#define VINIT int j; float a0, a1, f0, sum[DCTSIZE * 2]; \
	for (j = 0; j < n * 2; j++) sum[j] = 0;

#define VCORE \
	for (j = 0; j < n; j++) { \
		a0 = temp[y * n + j]; a1 = tab[y * n + j]; \
		f0 = (float)range - fabsf(a0); if (f0 < 0) f0 = 0; f0 *= f0; \
		a0 *= f0; a1 *= f0; a0 *= a1; a1 *= a1; \
		sum[j] += a0; sum[j + n] += a1; \
	}

#define VCORE1(sum) \
	((sum[0] + sum[4]) + (sum[1] + sum[5])) + \
	((sum[2] + sum[6]) + (sum[3] + sum[7]));
#define VFIN a2 += VCORE1(sum) a3 += VCORE1((sum+8))
#endif

	for (y = 0; y < n; y++) {
		border[y + n * 2] = image[y - stride];
		border[y + n * 3] = image[y + stride * n];
		border[y + n * 4] = image[y * stride - 1];
		border[y + n * 5] = image[y * stride + n];
	}

	for (k = n * n - 1; k > 0; k--) {
		int i = jpegqs_natural_order[k];
		float *tab = tables[i], a2 = 0, a3 = 0;
		int range = quantval[i] * 2;
		if (need_refresh && zigzag_refresh[i]) {
			idct_islow(coef, buf, n);
			need_refresh = 0;
#ifdef VINIT
			for (y = 0; y < n; y++) {
				border[y] = buf[y * n];
				border[y + n] = buf[y * n + n - 1];
			}

#ifndef VINITD
// same for SSE2, AVX2, AVX512
#define VINITD __m128i v0, v1, v2;
#define VDIFF(i) _mm_storeu_si128((__m128i*)&temp[(i) * n], _mm_sub_epi16(v0, v1));
#define VLDPIX(i, p) v##i = _mm_cvtepu8_epi16(_mm_loadl_epi64((__m128i*)(p)));
#define VRIGHT(a, b) v##a = _mm_bsrli_si128(v##b, 2);
#define VCOPY(a, b) v##a = v##b;
#endif

			{
				VINITD
				VLDPIX(0, buf)
				VLDPIX(1, border + n * 2)
				VDIFF(n)
				VRIGHT(1, 0) VDIFF(0)
				for (y = 1; y < n; y++) {
					VLDPIX(1, buf + y * n)
					VDIFF(y + n + 3)
					VCOPY(0, 1)
					VRIGHT(1, 0) VDIFF(y)
				}
				VLDPIX(1, border + n * 3)
				VDIFF(n + 1)
				VLDPIX(0, border)
				VLDPIX(1, border + n * 4)
				VDIFF(n + 2)
				VLDPIX(0, border + n)
				VLDPIX(1, border + n * 5)
				VDIFF(n + 3)

				if (flags & JPEGQS_DIAGONALS) {
					VLDPIX(0, buf)
					for (y = 0; y < n - 1; y++) {
						VLDPIX(2, buf + y * n + n)
						VRIGHT(1, 2)
						VDIFF(n * 2 + 4 + y * 2)
						VRIGHT(0, 0)
						VCOPY(1, 2)
						VDIFF(n * 2 + 4 + y * 2 + 1)
						VCOPY(0, 2)
					}
				}
			}
#undef VINITD
#undef VLDPIX
#undef VRIGHT
#undef VCOPY
#undef VDIFF
#endif
		}

#ifdef VINIT
#ifndef VINCR
#define VINCR 1
#endif
		{
			int y0 = i & (n - 1) ? 0 : n;
			int y1 = (i >= n ? n - 1 : 0) + n + 4;
			VINIT

			for (y = y0; y < y1; y += VINCR) { VCORE }
			if (flags & JPEGQS_DIAGONALS) {
				y0 = n * 2 + 4; y1 = y0 + (n - 1) * 2;
				for (y = y0; y < y1; y += VINCR) { VCORE }
			}

			VFIN
		}

#undef VINCR
#undef VINIT
#undef VCORE
#ifdef VCORE1
#undef VCORE1
#endif
#undef VFIN
#else
		{
			int p; float a0, a1, t;
#define CORE t = (float)range - fabsf(a0); \
	t = t < 0 ? 0 : t; t *= t; a0 *= t; a1 *= t; a2 += a0 * a1; a3 += a1 * a1;
#define M1(a, b) \
	for (y = 0; y < n - 1 + a; y++) \
	for (x = 0; x < n - 1 + b; x++) { p = y * n + x; \
	a0 = buf[p] - buf[(y + b) * n + x + a]; a1 = tab[p]; CORE }
#define M2(z, i) for (z = 0; z < n; z++) { p = y * n + x; \
	a0 = buf[p] - border[i * n + z]; a1 = *tab++; CORE }
			if (i & (n - 1)) M1(1, 0)
			tab += n * n;
			y = 0; M2(x, 2) y = n - 1; M2(x, 3)
			x = 0; M2(y, 4) x = n - 1; M2(y, 5)
			if (i > (n - 1)) M1(0, 1)

			if (flags & JPEGQS_DIAGONALS) {
				tab += n * n;
				for (y = 0; y < n - 1; y++, tab += n * 2)
				for (x = 0; x < n - 1; x++) {
					p = y * n + x;
					a0 = buf[p] - buf[p + n + 1]; a1 = tab[x]; CORE
					a0 = buf[p + 1] - buf[p + n]; a1 = tab[x + n]; CORE
				}
			}
#undef M2
#undef M1
#undef CORE
		}
#endif

		a2 = a2 / a3;
		range = roundf(a2);

		if (range) {
			int div = quantval[i], coef1 = coef[i], add;
			int dh, dl, d0 = (div - 1) >> 1, d1 = div >> 1;
			int a0 = (coef1 + (coef1 < 0 ? -d1 : d1)) / div * div;

			dh = a0 + (a0 < 0 ? d1 : d0);
			dl = a0 - (a0 > 0 ? d1 : d0);

			add = coef1 - range;
			if (add > dh) add = dh;
			if (add < dl) add = dl;
			coef[i] = add;
			need_refresh |= add ^ coef1;
		}
	}
end:
	if (flags & JPEGQS_NO_REBALANCE) return;
	if (!luma && flags & JPEGQS_NO_REBALANCE_UV) return;
#if 1 && defined(USE_NEON)
	if (sizeof(quantval[0]) == 2 && sizeof(quantval[0]) == sizeof(coef[0])) {
		JCOEF orig[DCTSIZE2]; int coef0 = coef[0];
		int32_t m0, m1; int32x4_t s0 = vdupq_n_s32(0), s1 = s0;
		coef[0] = 0;
		for (k = 0; k < DCTSIZE2; k += 8) {
			int16x8_t v0, v1, v2, v3; float32x4_t f0, f3, f4, f5; int32x4_t v4;
			v1 = vld1q_s16((int16_t*)&quantval[k]);
			v0 = vld1q_s16((int16_t*)&coef[k]); v3 = vshrq_n_s16(v0, 15);
			v2 = veorq_s16(vaddq_s16(vshrq_n_s16(v1, 1), v3), v3);
			v2 = vaddq_s16(v0, v2); f3 = vdupq_n_f32(0.5f); f5 = vnegq_f32(f3);
#define M1(low, f0) \
	v4 = vmovl_s16(vget_##low##_s16(v2)); \
	f0 = vbslq_f32(vreinterpretq_u32_s32(vshrq_n_s32(v4, 31)), f5, f3); \
	f4 = vcvtq_f32_s32(vmovl_s16(vget_##low##_s16(v1))); \
	f0 = vdivq_f32(vaddq_f32(vcvtq_f32_s32(v4), f0), f4);
			M1(low, f0) M1(high, f3)
#undef M1
			v2 = vcombine_s16(vmovn_s32(vcvtq_s32_f32(f0)), vmovn_s32(vcvtq_s32_f32(f3)));
			v2 = vmulq_s16(v2, v1);
			vst1q_s16((int16_t*)&orig[k], v2);
#define M1(low) \
	s0 = vmlal_s16(s0, vget_##low##_s16(v0), vget_##low##_s16(v2)); \
	s1 = vmlal_s16(s1, vget_##low##_s16(v2), vget_##low##_s16(v2));
			M1(low) M1(high)
#undef M1
		}
		{
#ifdef __aarch64__
			m0 = vaddvq_s32(s0); m1 = vaddvq_s32(s1);
#else
			int32x4x2_t v0 = vzipq_s32(s0, s1); int32x2_t v1;
			s0 = vaddq_s32(v0.val[0], v0.val[1]);
			v1 = vadd_s32(vget_low_s32(s0), vget_high_s32(s0));
			m0 = vget_lane_s32(v1, 0); m1 = vget_lane_s32(v1, 1);
#endif
		}
		if (m1 > m0) {
			int mul = (((int64_t)m1 << 13) + (m0 >> 1)) / m0;
			int16x8_t v4 = vdupq_n_s16(mul);
			for (k = 0; k < DCTSIZE2; k += 8) {
				int16x8_t v0, v1, v2, v3;
				v1 = vld1q_s16((int16_t*)&quantval[k]);
				v2 = vld1q_s16((int16_t*)&coef[k]);
				v2 = vqrdmulhq_s16(vshlq_n_s16(v2, 2), v4);
				v0 = vld1q_s16((int16_t*)&orig[k]);
				v3 = vaddq_s16(v1, vreinterpretq_s16_u16(vcgeq_s16(v0, vdupq_n_s16(0))));
				v2 = vminq_s16(v2, vaddq_s16(v0, vshrq_n_s16(v3, 1)));
				v3 = vaddq_s16(v1, vreinterpretq_s16_u16(vcleq_s16(v0, vdupq_n_s16(0))));
				v2 = vmaxq_s16(v2, vsubq_s16(v0, vshrq_n_s16(v3, 1)));
				vst1q_s16((int16_t*)&coef[k], v2);
			}
		}
		coef[0] = coef0;
	} else
#elif 1 && defined(USE_AVX2)
	if (sizeof(quantval[0]) == 2 && sizeof(quantval[0]) == sizeof(coef[0])) {
		JCOEF orig[DCTSIZE2]; int coef0 = coef[0];
		int32_t m0, m1; __m128i s0 = _mm_setzero_si128(), s1 = s0;
		coef[0] = 0;
		for (k = 0; k < DCTSIZE2; k += 8) {
			__m128i v0, v1, v2, v3; __m256i v4; __m256 f0;
			v1 = _mm_loadu_si128((__m128i*)&quantval[k]);
			v0 = _mm_loadu_si128((__m128i*)&coef[k]);
			v2 = _mm_srli_epi16(v1, 1); v3 = _mm_srai_epi16(v0, 15);
			v2 = _mm_xor_si128(_mm_add_epi16(v2, v3), v3);
			v2 = _mm_add_epi16(v0, v2);
			f0 = _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v2));
			f0 = _mm256_div_ps(f0, _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(v1)));
			v4 = _mm256_cvttps_epi32(f0);
			v2 = _mm_packs_epi32(_mm256_castsi256_si128(v4), _mm256_extractf128_si256(v4, 1));
			v2 = _mm_mullo_epi16(v2, v1);
			_mm_storeu_si128((__m128i*)&orig[k], v2);
			s0 = _mm_add_epi32(s0, _mm_madd_epi16(v0, v2));
			s1 = _mm_add_epi32(s1, _mm_madd_epi16(v2, v2));
		}
		s0 = _mm_hadd_epi32(s0, s1); s0 = _mm_hadd_epi32(s0, s0);
		m0 = _mm_cvtsi128_si32(s0); m1 = _mm_extract_epi32(s0, 1);
		if (m1 > m0) {
			int mul = (((int64_t)m1 << 13) + (m0 >> 1)) / m0;
			__m256i v4 = _mm256_set1_epi16(mul);
			for (k = 0; k < DCTSIZE2; k += 16) {
				__m256i v0, v1, v2, v3;
				v1 = _mm256_loadu_si256((__m256i*)&quantval[k]);
				v2 = _mm256_loadu_si256((__m256i*)&coef[k]);
				v2 = _mm256_mulhrs_epi16(_mm256_slli_epi16(v2, 2), v4);
				v0 = _mm256_loadu_si256((__m256i*)&orig[k]);
				v1 = _mm256_add_epi16(v1, _mm256_set1_epi16(-1));
				v3 = _mm256_sub_epi16(v1, _mm256_srai_epi16(v0, 15));
				v2 = _mm256_min_epi16(v2, _mm256_add_epi16(v0, _mm256_srai_epi16(v3, 1)));
				v3 = _mm256_sub_epi16(v1, _mm256_cmpgt_epi16(v0, _mm256_setzero_si256()));
				v2 = _mm256_max_epi16(v2, _mm256_sub_epi16(v0, _mm256_srai_epi16(v3, 1)));
				_mm256_storeu_si256((__m256i*)&coef[k], v2);
			}
		}
		coef[0] = coef0;
	} else
#elif 1 && defined(USE_SSE2)
	if (sizeof(quantval[0]) == 2 && sizeof(quantval[0]) == sizeof(coef[0])) {
		JCOEF orig[DCTSIZE2]; int coef0 = coef[0];
		int32_t m0, m1; __m128i s0 = _mm_setzero_si128(), s1 = s0;
		coef[0] = 0;
		for (k = 0; k < DCTSIZE2; k += 8) {
			__m128i v0, v1, v2, v3, v7; __m128 f0, f2, f4;
			v1 = _mm_loadu_si128((__m128i*)&quantval[k]);
			v0 = _mm_loadu_si128((__m128i*)&coef[k]);
			v2 = _mm_srli_epi16(v1, 1); v3 = _mm_srai_epi16(v0, 15);
			v2 = _mm_xor_si128(_mm_add_epi16(v2, v3), v3);
			v2 = _mm_add_epi16(v0, v2);
			v7 = _mm_setzero_si128(); v3 = _mm_srai_epi16(v2, 15);
#define M1(lo, f0) \
	f4 = _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v1, v7)); \
	f0 = _mm_cvtepi32_ps(_mm_unpack##lo##_epi16(v2, v3)); \
	f0 = _mm_div_ps(f0, f4);
		M1(lo, f0) M1(hi, f2)
#undef M1
			v2 = _mm_packs_epi32(_mm_cvttps_epi32(f0), _mm_cvttps_epi32(f2));
			v2 = _mm_mullo_epi16(v2, v1);
			_mm_storeu_si128((__m128i*)&orig[k], v2);
			s0 = _mm_add_epi32(s0, _mm_madd_epi16(v0, v2));
			s1 = _mm_add_epi32(s1, _mm_madd_epi16(v2, v2));
		}
#ifdef USE_SSE4
		s0 = _mm_hadd_epi32(s0, s1); s0 = _mm_hadd_epi32(s0, s0);
		m0 = _mm_cvtsi128_si32(s0); m1 = _mm_extract_epi32(s0, 1);
#else
		s0 = _mm_add_epi32(_mm_unpacklo_epi32(s0, s1), _mm_unpackhi_epi32(s0, s1));
		s0 = _mm_add_epi32(s0, _mm_bsrli_si128(s0, 8));
		m0 = _mm_cvtsi128_si32(s0); m1 = _mm_cvtsi128_si32(_mm_bsrli_si128(s0, 4));
#endif
		if (m1 > m0) {
			int mul = (((int64_t)m1 << 13) + (m0 >> 1)) / m0;
			__m128i v4 = _mm_set1_epi16(mul);
			for (k = 0; k < DCTSIZE2; k += 8) {
				__m128i v0, v1, v2, v3 = _mm_set1_epi16(-1);
				v1 = _mm_loadu_si128((__m128i*)&quantval[k]);
				v2 = _mm_loadu_si128((__m128i*)&coef[k]);
#ifdef USE_SSE4
				v2 = _mm_mulhrs_epi16(_mm_slli_epi16(v2, 2), v4);
#else
				v2 = _mm_mulhi_epi16(_mm_slli_epi16(v2, 4), v4);
				v2 = _mm_srai_epi16(_mm_sub_epi16(v2, v3), 1);
#endif
				v0 = _mm_loadu_si128((__m128i*)&orig[k]);
				v1 = _mm_add_epi16(v1, v3);
				v3 = _mm_sub_epi16(v1, _mm_srai_epi16(v0, 15));
				v2 = _mm_min_epi16(v2, _mm_add_epi16(v0, _mm_srai_epi16(v3, 1)));
				v3 = _mm_sub_epi16(v1, _mm_cmpgt_epi16(v0, _mm_setzero_si128()));
				v2 = _mm_max_epi16(v2, _mm_sub_epi16(v0, _mm_srai_epi16(v3, 1)));
				_mm_storeu_si128((__m128i*)&coef[k], v2);
			}
		}
		coef[0] = coef0;
	} else
#endif
	{
		JCOEF orig[DCTSIZE2];
		int64_t m0 = 0, m1 = 0;
		for (k = 1; k < DCTSIZE2; k++) {
			int div = quantval[k], coef1 = coef[k], d1 = div >> 1;
			int a0 = (coef1 + (coef1 < 0 ? -d1 : d1)) / div * div;
			orig[k] = a0;
			m0 += coef1 * a0; m1 += a0 * a0;
		}
		if (m1 > m0) {
			int mul = ((m1 << 13) + (m0 >> 1)) / m0;
			for (k = 1; k < DCTSIZE2; k++) {
				int div = quantval[k], coef1 = coef[k], add;
				int dh, dl, d0 = (div - 1) >> 1, d1 = div >> 1;
				int a0 = orig[k];

				dh = a0 + (a0 < 0 ? d1 : d0);
				dl = a0 - (a0 > 0 ? d1 : d0);

				add = (coef1 * mul + 0x1000) >> 13;
				if (add > dh) add = dh;
				if (add < dl) add = dl;
				coef[k] = add;
			}
		}
	}
}


static int64_t get_time_usec() {
	struct timeval time;
	gettimeofday(&time, NULL);
	return time.tv_sec * (int64_t)1000000 + time.tv_usec;
}


int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length)
{
    sft_t sft;
    uint8_t req[MAX_MESSAGE_LENGTH];
    int req_length;

    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    if (raw_req_length < 2 || raw_req_length > (MODBUS_MAX_PDU_LENGTH + 1)) {
        /* The raw request must contain function and slave at least and
           must not be longer than the maximum pdu length plus the slave
           address. */
        errno = EINVAL;
        return -1;
    }

    sft.slave = raw_req[0];
    sft.function = raw_req[1];
    /* The t_id is left to zero */
    sft.t_id = 0;
    /* This response function only set the header so it's convenient here */
    req_length = ctx->backend->build_response_basis(&sft, req);

    if (raw_req_length > 2) {
        /* Copy data after function code */
        memcpy(req + req_length, raw_req + 2, raw_req_length - 2);
        req_length += raw_req_length - 2;
    }

    return send_msg(ctx, req, req_length);
}


static int looks_like_pathspec(const char *arg)
{
	const char *p;
	int escaped = 0;

	/*
	 * Wildcard characters imply the user is looking to match pathspecs
	 * that aren't in the filesystem. Note that this doesn't include
	 * backslash even though it's a glob special; by itself it doesn't
	 * cause any increase in the match. Likewise ignore backslash-escaped
	 * wildcard characters.
	 */
	for (p = arg; *p; p++) {
		if (escaped) {
			escaped = 0;
		} else if (is_glob_special(*p)) {
			if (*p == '\\')
				escaped = 1;
			else
				return 1;
		}
	}

	/* long-form pathspec magic */
	if (starts_with(arg, ":("))
		return 1;

	return 0;
}


int verify_repository_format(const struct repository_format *format,
			     struct strbuf *err)
{
	if (GIT_REPO_VERSION_READ < format->version) {
		strbuf_addf(err, _("Expected git repo version <= %d, found %d"),
			    GIT_REPO_VERSION_READ, format->version);
		return -1;
	}

	if (format->version >= 1 && format->unknown_extensions.nr) {
		int i;

		strbuf_addstr(err, _("unknown repository extensions found:"));

		for (i = 0; i < format->unknown_extensions.nr; i++)
			strbuf_addf(err, "\n\t%s",
				    format->unknown_extensions.items[i].string);
		return -1;
	}

	if (format->version == 0 && format->v1_only_extensions.nr) {
		int i;

		strbuf_addstr(err,
			      _("repo version is 0, but v1-only extensions found:"));

		for (i = 0; i < format->v1_only_extensions.nr; i++)
			strbuf_addf(err, "\n\t%s",
				    format->v1_only_extensions.items[i].string);
		return -1;
	}

	return 0;
}


xfs_fsbulkstat_one_fmt(
	struct xfs_ibulk		*breq,
	const struct xfs_bulkstat	*bstat)
{
	struct xfs_bstat		bs1;

	xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
	if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1)))
		return -EFAULT;
	return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat));
}


xfs_bulk_ireq_teardown(
	struct xfs_bulk_ireq	*hdr,
	struct xfs_ibulk	*breq)
{
	hdr->ino = breq->startino;
	hdr->ocount = breq->ocount;
}


xfs_ioc_attr_put_listent(
	struct xfs_attr_list_context *context,
	int			flags,
	unsigned char		*name,
	int			namelen,
	int			valuelen)
{
	struct xfs_attrlist	*alist = context->buffer;
	struct xfs_attrlist_ent	*aep;
	int			arraytop;

	ASSERT(!context->seen_enough);
	ASSERT(context->count >= 0);
	ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
	ASSERT(context->firstu >= sizeof(*alist));
	ASSERT(context->firstu <= context->bufsize);

	/*
	 * Only list entries in the right namespace.
	 */
	if (context->attr_filter != (flags & XFS_ATTR_NSP_ONDISK_MASK))
		return;

	arraytop = sizeof(*alist) +
			context->count * sizeof(alist->al_offset[0]);

	/* decrement by the actual bytes used by the attr */
	context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) +
			namelen + 1, sizeof(uint32_t));
	if (context->firstu < arraytop) {
		trace_xfs_attr_list_full(context);
		alist->al_more = 1;
		context->seen_enough = 1;
		return;
	}

	aep = context->buffer + context->firstu;
	aep->a_valuelen = valuelen;
	memcpy(aep->a_name, name, namelen);
	aep->a_name[namelen] = 0;
	alist->al_offset[context->count++] = context->firstu;
	alist->al_count = context->count;
	trace_xfs_attr_list_add(context);
}


xfs_ioctl_setattr_get_trans(
	struct xfs_inode	*ip,
	struct xfs_dquot	*pdqp)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
	int			error = -EROFS;

	if (xfs_is_readonly(mp))
		goto out_error;
	error = -EIO;
	if (xfs_is_shutdown(mp))
		goto out_error;

	error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
			capable(CAP_FOWNER), &tp);
	if (error)
		goto out_error;

	if (xfs_has_wsync(mp))
		xfs_trans_set_sync(tp);

	return tp;

out_error:
	return ERR_PTR(error);
}


xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fileattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
	xfs_failaddr_t		failaddr;
	uint16_t		new_diflags;

	if (!fa->fsx_valid)
		return 0;

	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents &&
	    XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
		return -EINVAL;

	if (fa->fsx_extsize & mp->m_blockmask)
		return -EINVAL;

	new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);

	/*
	 * Inode verifiers do not check that the extent size hint is an integer
	 * multiple of the rt extent size on a directory with both rtinherit
	 * and extszinherit flags set.  Don't let sysadmins misconfigure
	 * directories.
	 */
	if ((new_diflags & XFS_DIFLAG_RTINHERIT) &&
	    (new_diflags & XFS_DIFLAG_EXTSZINHERIT)) {
		unsigned int	rtextsize_bytes;

		rtextsize_bytes = XFS_FSB_TO_B(mp, mp->m_sb.sb_rextsize);
		if (fa->fsx_extsize % rtextsize_bytes)
			return -EINVAL;
	}

	failaddr = xfs_inode_validate_extsize(ip->i_mount,
			XFS_B_TO_FSB(mp, fa->fsx_extsize),
			VFS_I(ip)->i_mode, new_diflags);
	return failaddr != NULL ? -EINVAL : 0;
}


xfs_ioc_scrub_metadata(
	struct file			*file,
	void				__user *arg)
{
	struct xfs_scrub_metadata	scrub;
	int				error;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (copy_from_user(&scrub, arg, sizeof(scrub)))
		return -EFAULT;

	error = xfs_scrub_metadata(file, &scrub);
	if (error)
		return error;

	if (copy_to_user(arg, &scrub, sizeof(scrub)))
		return -EFAULT;

	return 0;
}


TPMT_HA_Unmarshal(TPMT_HA *target, BYTE **buffer, INT32 *size, BOOL allowNull)
{
    TPM_RC rc = TPM_RC_SUCCESS;

    if (rc == TPM_RC_SUCCESS) {
	rc = TPMI_ALG_HASH_Unmarshal(&target->hashAlg, buffer, size, allowNull);
    }
    if (rc == TPM_RC_SUCCESS) {
	rc = TPMU_HA_Unmarshal(&target->digest, buffer, size, target->hashAlg);
    }
    return rc;
}


TPMI_SH_HMAC_Unmarshal(TPMI_SH_HMAC *target, BYTE **buffer, INT32 *size)
{
    TPM_RC rc = TPM_RC_SUCCESS;
    TPMI_SH_HMAC orig_target = *target; // libtpms added

    if (rc == TPM_RC_SUCCESS) {
	rc = TPM_HANDLE_Unmarshal(target, buffer, size);
    }
    if (rc == TPM_RC_SUCCESS) {
	BOOL isNotHmacSession = (*target < HMAC_SESSION_FIRST ) || (*target > HMAC_SESSION_LAST);
	if (isNotHmacSession) {
	    rc = TPM_RC_VALUE;
	    *target = orig_target; // libtpms added
	}
    }
    return rc;
}


TPMI_RH_HIERARCHY_POLICY_Unmarshal(TPMI_RH_HIERARCHY_POLICY *target, BYTE **buffer, INT32 *size)
{
    TPM_RC rc = TPM_RC_SUCCESS;
    TPMI_RH_HIERARCHY_POLICY orig_target = *target; // libtpms added

    if (rc == TPM_RC_SUCCESS) {
	rc = TPM_HANDLE_Unmarshal(target, buffer, size);
    }
    if (rc == TPM_RC_SUCCESS) {
	switch (*target) {
	  case TPM_RH_OWNER:
	  case TPM_RH_PLATFORM:
	  case TPM_RH_ENDORSEMENT:
	  case TPM_RH_LOCKOUT:
	    break;
	  default:
	      {
		  BOOL isNotHP =  (*target < TPM_RH_ACT_0) || (*target > TPM_RH_ACT_F);
		  if (isNotHP) {
		      rc = TPM_RC_VALUE;
		      *target = orig_target; // libtpms added
		  }
	      }
	}
    }
    return rc;
}


TPMI_RH_NV_INDEX_Unmarshal(TPMI_RH_NV_INDEX *target, BYTE **buffer, INT32 *size)
{
    TPM_RC rc = TPM_RC_SUCCESS;
    TPMI_RH_NV_INDEX orig_target = *target; // libtpms added

    if (rc == TPM_RC_SUCCESS) {
	rc = TPM_HANDLE_Unmarshal(target, buffer, size);
    }
    if (rc == TPM_RC_SUCCESS) {
	BOOL isNotNv = (*target < NV_INDEX_FIRST) || (*target > NV_INDEX_LAST);
	if (isNotNv) {
	    rc = TPM_RC_VALUE;
	    *target = orig_target; // libtpms added
	}
    }
    return rc;
}


TPMI_DH_OBJECT_Unmarshal(TPMI_DH_OBJECT *target, BYTE **buffer, INT32 *size, BOOL allowNull)
{
    TPM_RC rc = TPM_RC_SUCCESS;
    TPMI_DH_OBJECT orig_target = *target; // libtpms added

    if (rc == TPM_RC_SUCCESS) {
	rc = TPM_HANDLE_Unmarshal(target, buffer, size);
    }
    if (rc == TPM_RC_SUCCESS) {
	BOOL isNotTransient = (*target < TRANSIENT_FIRST) || (*target > TRANSIENT_LAST);
	BOOL isNotPersistent = (*target < PERSISTENT_FIRST) || (*target > PERSISTENT_LAST);
	BOOL isNotLegalNull = (*target != TPM_RH_NULL) || !allowNull;
	if (isNotTransient &&
	    isNotPersistent &&
	    isNotLegalNull) {
	    rc = TPM_RC_VALUE;
	    *target = orig_target; // libtpms added
	}
    }
    return rc;
}


TPMI_AES_KEY_BITS_Unmarshal(TPMI_AES_KEY_BITS *target, BYTE **buffer, INT32 *size)
{
    TPM_RC rc = TPM_RC_SUCCESS;
    TPMI_AES_KEY_BITS orig_target = *target; // libtpms added

    if (rc == TPM_RC_SUCCESS) {
	rc = TPM_KEY_BITS_Unmarshal(target, buffer, size);
    }
    if (rc == TPM_RC_SUCCESS) {
	switch (*target) {
	  case 128:
	  case 256:
	    break;
	  default:
	    rc = TPM_RC_VALUE;
	    *target = orig_target; // libtpms added
	}
    }
    return rc;
}