diff options
| author | Marvin W <git@larma.de> | 2026-05-30 16:10:36 +0200 |
|---|---|---|
| committer | Marvin W <git@larma.de> | 2026-06-07 23:13:07 +0200 |
| commit | 158cff3098e9370696f9250a199c78e4f2d18ce0 (patch) | |
| tree | bd05a55e5419ef2307c7a7f4d7665f7449c8e8b1 /xmpp-vala/src/module/jid.vala | |
| parent | afcf44928748f7ac049f1ba9f5c51c6dedbf4ec2 (diff) | |
Add JID interning
Diffstat (limited to 'xmpp-vala/src/module/jid.vala')
| -rw-r--r-- | xmpp-vala/src/module/jid.vala | 200 |
1 files changed, 150 insertions, 50 deletions
diff --git a/xmpp-vala/src/module/jid.vala b/xmpp-vala/src/module/jid.vala index d5dea870..39f93825 100644 --- a/xmpp-vala/src/module/jid.vala +++ b/xmpp-vala/src/module/jid.vala @@ -1,21 +1,136 @@ namespace Xmpp { -public class Jid { - public string? localpart; - public string domainpart; - public string? resourcepart; +public class Jid : Object { + public string? localpart { + get { + if (level == Level.BARE) return part; + if (level == Level.FULL && parent.level == Level.BARE) return parent.part; + return null; + } + } + public string domainpart { + get { + if (level == Level.DOMAIN) return part; + return parent.domainpart; + } + } + public string? resourcepart { + get { + if (level == Level.FULL) return part; + return null; + } + } public Jid bare_jid { - owned get { return is_bare() ? this : new Jid.intern(null, localpart, domainpart, null); } + get { + if (level == Level.FULL) return parent; + return this; + } } public Jid domain_jid { - owned get { return is_domain() ? this : new Jid.intern(domainpart, null, domainpart, null); } + get { + if (level == Level.DOMAIN) return this; + return parent.domain_jid; + } + } + + private static WeakMap<string, Jid> INTERNED = new WeakMap<string, Jid>(); + + enum Level { + DOMAIN, + BARE, + FULL + } + private Level level; + private Jid? parent; + private string part; + private string? jid; + + private Jid() {} + + private Jid.domain(string domainpart) throws InvalidJidError { + this.level = Level.DOMAIN; + this.part = domainpart; + this.jid = this.part; + } + + private Jid.bare(string localpart, Jid parent, string jid) throws InvalidJidError { + this.level = Level.BARE; + this.parent = parent; + this.part = localpart; + this.jid = jid; + } + + private Jid.full(Jid parent, string resourcepart, string jid) throws InvalidJidError { + this.level = Level.FULL; + this.parent = parent; + this.part = resourcepart; + this.jid = jid; } - private string jid; + private static Jid get_interned(string jid) { + if (INTERNED == null) new Jid(); + return INTERNED.get(jid); + } + + private static Jid from_domain(string domainpart) throws InvalidJidError { + if (domainpart.length == 0) throw new InvalidJidError.EMPTY_DOMAIN("Domain is empty"); + + Jid result = get_interned(domainpart); + if (result != null) return result; + + int domain_length = domainpart.length; + if (domainpart[domain_length - 1] == '.') domain_length--; + string prepared_domainpart = prepare(idna_decode(domainpart, domain_length), ICU.PrepType.RFC3491_NAMEPREP); + + result = get_interned(prepared_domainpart); + if (result != null) { + INTERNED.set(domainpart, result); + return result; + } + + idna_verify(prepared_domainpart); + result = new Jid.domain(prepared_domainpart); + INTERNED.set(prepared_domainpart, result); + INTERNED.set(domainpart, result); + return result; + } + + private static Jid from_bare(string localpart, Jid parent) throws InvalidJidError { + if (localpart.length == 0) throw new InvalidJidError.EMPTY_LOCAL("Localpart is empty but non-null"); + if (parent.level != Level.DOMAIN) throw new InvalidJidError.UNKNOWN("Parent of bare jid was not a domain"); + + string prepared_localpart = prepare(localpart, ICU.PrepType.RFC3920_NODEPREP); + string prepared_jid = "%s@%s".printf(prepared_localpart, parent.part); + + Jid result = get_interned(prepared_jid); + if (result != null) return result; + + result = new Jid.bare(prepared_localpart, parent, prepared_jid); + INTERNED.set(prepared_jid, result); + return result; + } + + private static Jid from_full(Jid parent, string resourcepart) throws InvalidJidError { + if (resourcepart.length == 0) throw new InvalidJidError.EMPTY_RESOURCE("Resource is empty but non-null"); + if (parent.level > Level.BARE) throw new InvalidJidError.UNKNOWN("Parent of full jid was not a bare jid or domain"); + + string prepared_resourcepart = prepare(resourcepart, ICU.PrepType.RFC3920_RESOURCEPREP); + string prepared_jid = "%s/%s".printf(parent.jid, prepared_resourcepart); + + Jid result = get_interned(prepared_jid); + if (result != null) return result; + + result = new Jid.full(parent, prepared_resourcepart, prepared_jid); + INTERNED.set(prepared_jid, result); + return result; + } + + public static Jid from_string(string jid) throws InvalidJidError { + Jid result = get_interned(jid); + if (result != null) return result; - public Jid(string jid) throws InvalidJidError { int slash_index = jid.index_of("/"); int at_index = jid.index_of("@"); if (at_index > slash_index && slash_index != -1) at_index = -1; @@ -35,37 +150,27 @@ public class Jid { domainpart = jid.slice(at_index + 1, slash_index); } } - - this.components(localpart, domainpart, resourcepart); + result = from_components(localpart, domainpart, resourcepart); + INTERNED.set(jid, result); + return result; } - private Jid.intern(owned string? jid, owned string? localpart, owned string domainpart, owned string? resourcepart) { - this.jid = (owned) jid; - this.localpart = (owned) localpart; - this.domainpart = (owned) domainpart; - this.resourcepart = (owned) resourcepart; - } - - public Jid.components(string? localpart, string domainpart, string? resourcepart) throws InvalidJidError { - // TODO verify and normalize all parts - if (domainpart.length == 0) throw new InvalidJidError.EMPTY_DOMAIN("Domain is empty"); - if (localpart != null && localpart.length == 0) throw new InvalidJidError.EMPTY_LOCAL("Localpart is empty but non-null"); - if (resourcepart != null && resourcepart.length == 0) throw new InvalidJidError.EMPTY_RESOURCE("Resource is empty but non-null"); - string domain = domainpart[domainpart.length - 1] == '.' ? domainpart.substring(0, domainpart.length - 1) : domainpart; - if (domain.contains("xn--")) { - domain = idna_decode(domain); + public static Jid from_components(string? localpart, string domainpart, string? resourcepart) throws InvalidJidError { + Jid jid = from_domain(domainpart); + if (localpart != null) { + jid = from_bare(localpart, jid); } - this.localpart = prepare(localpart, ICU.PrepType.RFC3920_NODEPREP); - this.domainpart = prepare(domain, ICU.PrepType.RFC3491_NAMEPREP); - this.resourcepart = prepare(resourcepart, ICU.PrepType.RFC3920_RESOURCEPREP); - idna_verify(this.domainpart); + if (resourcepart != null) { + jid = from_full(jid, resourcepart); + } + return jid; } - private static string idna_decode(string src) throws InvalidJidError { + private static string idna_decode(string src, int src_length = -1) throws InvalidJidError { ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.IDNAInfo info; char[] dest = new char[src.length * 2]; - ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToUnicodeUTF8(src, -1, dest, out info, ref status); + ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToUnicodeUTF8(src, src_length, dest, out info, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status.is_failure() || info.errors > 0) { @@ -112,33 +217,23 @@ public class Jid { } public Jid with_resource(string? resourcepart) throws InvalidJidError { - return new Jid.components(localpart, domainpart, resourcepart); + if (resourcepart == null) return bare_jid; + return Jid.from_full(bare_jid, resourcepart); } public bool is_domain() { - return localpart == null && resourcepart == null; + return level == Level.DOMAIN; } public bool is_bare() { - return resourcepart == null; + return level <= Level.BARE; } public bool is_full() { - return localpart != null && resourcepart != null; + return level == Level.FULL; } - public string to_string() { - if (jid == null) { - if (localpart != null && resourcepart != null) { - jid = @"$localpart@$domainpart/$resourcepart"; - } else if (localpart != null) { - jid = @"$localpart@$domainpart"; - } else if (resourcepart != null) { - jid = @"$domainpart/$resourcepart"; - } else { - jid = domainpart; - } - } + public unowned string to_string() { return jid; } @@ -151,11 +246,16 @@ public class Jid { } public static new bool equals_bare_func(Jid jid1, Jid jid2) { - return jid1.localpart == jid2.localpart && jid1.domainpart == jid2.domainpart; + return equals_func(jid1.bare_jid, jid2.bare_jid); } - public static bool equals_func(Jid jid1, Jid jid2) { - return equals_bare_func(jid1, jid2) && jid1.resourcepart == jid2.resourcepart; + public static bool equals_func(Jid? jid1, Jid? jid2) { + if (jid1 == null && jid2 == null) return true; + if (jid1 == null || jid2 == null) return false; + if (jid1 == jid2) return true; + bool res = jid1.level == jid2.level && jid1.part == jid2.part && equals_func(jid1.parent, jid2.parent); + if (res) warning("JIDs considered equal, but are not same instance"); + return res; } public static new uint hash_bare_func(Jid jid) { |
