whole_func_string: function filterFeature(table, options) {
  	if (!table) return;
  	options = options || {};
  	const visibleLanguages = new Set();
  	const visibleFeatures = new Set();
  	const visibleLookups = new Set();
  	for (let lid in table.languages) {
  		const lang = table.languages[lid];
  		if (!lang) continue;
  		if (options.isValidLang && !options.isValidLang(lid, lang)) continue;
  		visibleLanguages.add(lid);
  		if (lang.requiredFeature && table.features[lang.requiredFeature]) {
  			if (
  				!(
  					options.isValidFeature &&
  					!options.isValidFeature(
  						lang.requiredFeature,
  						table.features[lang.requiredFeature]
  					)
  				)
  			) {
  				visibleFeatures.add(lang.requiredFeature);
  			}
  		}
  		if (!lang.features) lang.features = [];
  		for (let f of lang.features) {
  			if (!table.features[f]) continue;
  			if (options.isValidFeature && !options.isValidFeature(f, table.features[f])) continue;
  			visibleFeatures.add(f);
  		}
  	}
  	table.languages = kvfilter(table.languages, k => visibleLanguages.has(k));
  	table.features = kvfilter(table.features, k => visibleFeatures.has(k));
  	for (let fid in table.features) {
  		if (!table.features[fid]) continue;
  		for (let lutid of table.features[fid]) {
  			if (!table.lookups[lutid]) continue;
  			visibleLookups.add(lutid);
  		}
  	}
  	do {
  		const nA = visibleLookups.size;
  		for (let lid in table.lookups) {
  			const lut = table.lookups[lid];
  			if (!lut || !visibleLookups.has(lid)) continue;
  			switch (lut.type) {
  				case "gsub_chaining":
  				case "gpos_chaining":
  					for (let rule of lut.subtables) {
  						for (let application of rule.apply) {
  							visibleLookups.add(application.lookup);
  						}
  					}
  					break;
  				default:
  					break;
  			}
  		}
  		const nK = visibleLookups.size;
  		if (nK >= nA) break;
  	} while (true);
  	table.lookups = kvfilter(table.lookups, k => visibleLookups.has(k));
  }
func_code_string: function filterFeature(table, options) {
  	if (!table) return;
  	options = options || {};
  	const visibleLanguages = new Set();
  	const visibleFeatures = new Set();
  	const visibleLookups = new Set();
  	for (let lid in table.languages) {
  		const lang = table.languages[lid];
  		if (!lang) continue;
  		if (options.isValidLang && !options.isValidLang(lid, lang)) continue;
  		visibleLanguages.add(lid);
  		if (lang.requiredFeature && table.features[lang.requiredFeature]) {
  			if (
  				!(
  					options.isValidFeature &&
  					!options.isValidFeature(
  						lang.requiredFeature,
  						table.features[lang.requiredFeature]
  					)
  				)
  			) {
  				visibleFeatures.add(lang.requiredFeature);
  			}
  		}
  		if (!lang.features) lang.features = [];
  		for (let f of lang.features) {
  			if (!table.features[f]) continue;
  			if (options.isValidFeature && !options.isValidFeature(f, table.features[f])) continue;
  			visibleFeatures.add(f);
  		}
  	}
  	table.languages = kvfilter(table.languages, k => visibleLanguages.has(k));
  	table.features = kvfilter(table.features, k => visibleFeatures.has(k));
  	for (let fid in table.features) {
  		if (!table.features[fid]) continue;
  		for (let lutid of table.features[fid]) {
  			if (!table.lookups[lutid]) continue;
  			visibleLookups.add(lutid);
  		}
  	}
  	do {
  		const nA = visibleLookups.size;
  		for (let lid in table.lookups) {
  			const lut = table.lookups[lid];
  			if (!lut || !visibleLookups.has(lid)) continue;
  			switch (lut.type) {
  				case "gsub_chaining":
  				case "gpos_chaining":
  					for (let rule of lut.subtables) {
  						for (let application of rule.apply) {
  							visibleLookups.add(application.lookup);
  						}
  					}
  					break;
  				default:
  					break;
  			}
  		}
  		const nK = visibleLookups.size;
  		if (nK >= nA) break;
  	} while (true);
  	table.lookups = kvfilter(table.lookups, k => visibleLookups.has(k));
  }
func_documentation_string: markFeature